From 9655d9dfb3463368179d42a177db4170ec9d2e3a Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Mon, 12 Jul 2021 17:43:30 +0200
Subject: [PATCH] WIP: more functionality for the c interface

---
 CMakeLists.txt                            |  23 +++-
 include/CMakeLists.txt                    |   2 +-
 include/caosdb/authentication.h           |   8 +-
 include/caosdb/connection.h               |  55 ++++-----
 include/caosdb/{utils.h => utility.h}     |   4 +-
 include/ccaosdb.h                         | 115 +++++++++++++++----
 src/caosdb/authentication.cpp             |   4 +-
 src/caosdb/connection.cpp                 |  75 ++++++------
 src/ccaosdb.cpp                           | 132 +++++++++++++++++++---
 src/ccaosdbcli.c                          |  38 ++++++-
 src/cxxcaosdbcli.cpp                      |  22 ++--
 test/CMakeLists.txt                       |   2 +-
 test/test_ccaosdb.cpp                     |   4 +-
 test/test_connection.cpp                  |  11 +-
 test/{test_utils.cpp => test_utility.cpp} |  10 +-
 15 files changed, 366 insertions(+), 139 deletions(-)
 rename include/caosdb/{utils.h => utility.h} (98%)
 rename test/{test_utils.cpp => test_utility.cpp} (91%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffdd2f0..2cfac1c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,7 +100,6 @@ foreach(i RANGE "${len_proto_files}")
         "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.grpc.pb.h")
     list(APPEND GRPC_GENERATED_HEADERS "${next_proto_hdr}" "${next_grpc_hdr}")
     list(APPEND GRPC_GENERATED_SOURCES "${next_proto_src}" "${next_grpc_src}")
-
 endforeach()
 
 set(GRPC_GENERATED
@@ -295,7 +294,27 @@ install(
 )
 
 install(FILES ${libcaosdb_INCL} ${PROJECT_SOURCE_DIR}/include/ccaosdb.h DESTINATION ${libcaosdb_INCLUDE_DEST})
-install(FILES ${GRPC_GENERATED_HEADERS} DESTINATION ${libcaosdb_INCLUDE_DEST})
+foreach(i RANGE "${len_proto_files}")
+    list(GET PROTO_FILES ${i} next_proto_file)
+
+    # strip away the prefix path and the ".proto" suffix
+    string(REPLACE
+        "${PROJECT_SOURCE_DIR}/proto/proto/caosdb/"
+        ""
+        next_proto_module
+        "${next_proto_file}")
+    string(REPLACE
+        "/main.proto"
+        ""
+        next_proto_module
+        "${next_proto_module}")
+    set(next_proto_hdr
+        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.pb.h")
+    set(next_grpc_hdr
+        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.grpc.pb.h")
+    install(FILES ${next_proto_hdr} ${next_grpc_hdr} DESTINATION
+        ${libcaosdb_INCLUDE_DEST}/${next_proto_module})
+endforeach()
 
 install(FILES ${PROJECT_SOURCE_DIR}/caosdbConfig.cmake
     DESTINATION ${libcaosdb_CMAKE_DEST})
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 89657f1..dcc501e 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -27,7 +27,7 @@ set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/exceptions.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/info.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utils.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.h
     )
 
 # pass variable to parent scope
diff --git a/include/caosdb/authentication.h b/include/caosdb/authentication.h
index d8c3595..703fcf5 100644
--- a/include/caosdb/authentication.h
+++ b/include/caosdb/authentication.h
@@ -32,7 +32,7 @@
 #include <map>                                         // for multimap
 #include <memory>                                      // for shared_ptr
 #include <string>                                      // for string
-#include "caosdb/utils.h"                              // for base64_encode
+#include "caosdb/utility.h"                            // for base64_encode
 #include "grpcpp/impl/codegen/interceptor.h"           // for Status
 #include "grpcpp/impl/codegen/security/auth_context.h" // for AuthContext
 #include "grpcpp/impl/codegen/status.h"                // for Status
@@ -40,7 +40,7 @@
 
 namespace caosdb {
 namespace authentication {
-using caosdb::utils::base64_encode;
+using caosdb::utility::base64_encode;
 using grpc::AuthContext;
 using grpc::MetadataCredentialsPlugin;
 using grpc::Status;
@@ -51,7 +51,7 @@ using grpc::string_ref;
  */
 class Authenticator {
 public:
-  [[nodiscard]] virtual auto getCallCredentials() const
+  [[nodiscard]] virtual auto GetCallCredentials() const
     -> std::shared_ptr<grpc::CallCredentials> = 0;
 };
 
@@ -82,7 +82,7 @@ public:
   PlainPasswordAuthenticator(const std::string &username,
                              const std::string &password);
 
-  [[nodiscard]] auto getCallCredentials() const
+  [[nodiscard]] auto GetCallCredentials() const
     -> std::shared_ptr<grpc::CallCredentials> override;
 };
 } // namespace authentication
diff --git a/include/caosdb/connection.h b/include/caosdb/connection.h
index 0e722a5..7f45319 100644
--- a/include/caosdb/connection.h
+++ b/include/caosdb/connection.h
@@ -30,19 +30,13 @@
 #include <iosfwd>                                // for ostream
 #include <memory>                                // for shared_ptr, unique_ptr
 #include <string>                                // for string
+#include "caosdb/authentication.h"               // for Authenticator
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
+#include "caosdb/info.h"                         // for VersionInfo
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"   // for GeneralInfoService:...
 #include "caosdb/transaction.h"                  // for Transaction
-#include "grpcpp/impl/codegen/client_callback.h" // for Channel
-namespace caosdb::authentication {
-class Authenticator;
-} // namespace caosdb::authentication
-namespace caosdb::info {
-class VersionInfo;
-} // namespace caosdb::info
-namespace grpc {
-class ChannelCredentials;
-} // namespace grpc
+#include "grpcpp/channel.h"                      // for Channel
+#include "grpcpp/security/credentials.h"         // for ChannelCredentials
 
 namespace caosdb::connection {
 using caosdb::authentication::Authenticator;
@@ -54,7 +48,7 @@ using grpc::ChannelCredentials;
 
 class CACertificateProvider {
 public:
-  [[nodiscard]] auto virtual getCACertPem() const -> std::string = 0;
+  [[nodiscard]] auto virtual GetCACertPem() const -> std::string = 0;
 };
 
 class PemFileCACertProvider : public CACertificateProvider {
@@ -63,7 +57,7 @@ private:
 
 public:
   explicit PemFileCACertProvider(const std::string &path);
-  [[nodiscard]] auto getCACertPem() const -> std::string override;
+  [[nodiscard]] auto GetCACertPem() const -> std::string override;
 };
 
 class PemCACertProvider : public CACertificateProvider {
@@ -72,7 +66,7 @@ private:
 
 public:
   explicit PemCACertProvider(const std::string &cacert);
-  [[nodiscard]] auto getCACertPem() const -> std::string override;
+  [[nodiscard]] auto GetCACertPem() const -> std::string override;
 };
 
 /**
@@ -89,10 +83,10 @@ public:
                          const CaosDBConnectionConfig &config)
     -> std::ostream &;
 
-  [[nodiscard]] auto virtual toString() const -> std::string = 0;
-  [[nodiscard]] auto getHost() const -> std::string;
-  [[nodiscard]] auto getPort() const -> int;
-  [[nodiscard]] auto virtual getChannelCredentials() const
+  [[nodiscard]] auto virtual ToString() const -> std::string = 0;
+  [[nodiscard]] auto GetHost() const -> std::string;
+  [[nodiscard]] auto GetPort() const -> int;
+  [[nodiscard]] auto virtual GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> = 0;
 };
 
@@ -102,9 +96,9 @@ private:
 
 public:
   InsecureCaosDBConnectionConfig(const std::string &host, int port);
-  [[nodiscard]] auto getChannelCredentials() const
+  [[nodiscard]] auto GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto toString() const -> std::string override;
+  [[nodiscard]] auto ToString() const -> std::string override;
 };
 
 class SslCaosDBConnectionConfig : public CaosDBConnectionConfig {
@@ -113,16 +107,17 @@ private:
   std::string cacert;
 
 public:
-  SslCaosDBConnectionConfig(
-    const std::string &host, int port,
-    const std::shared_ptr<CACertificateProvider> &cacert);
-  SslCaosDBConnectionConfig(
-    const std::string &host, int port,
-    const std::shared_ptr<CACertificateProvider> &cacert,
-    const std::shared_ptr<Authenticator> &authenticator);
-  [[nodiscard]] auto getChannelCredentials() const
+  SslCaosDBConnectionConfig(const std::string &host, int port);
+  SslCaosDBConnectionConfig(const std::string &host, int port,
+                            const Authenticator &authenticator);
+  SslCaosDBConnectionConfig(const std::string &host, int port,
+                            const CACertificateProvider &cacert);
+  SslCaosDBConnectionConfig(const std::string &host, int port,
+                            const CACertificateProvider &cacert,
+                            const Authenticator &authenticator);
+  [[nodiscard]] auto GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto toString() const -> std::string override;
+  [[nodiscard]] auto ToString() const -> std::string override;
 };
 
 /**
@@ -130,13 +125,11 @@ public:
  */
 class CaosDBConnection {
   std::shared_ptr<grpc::Channel> channel;
-  std::shared_ptr<CaosDBConnectionConfig> config;
   std::unique_ptr<GeneralInfoService::Stub> general_info_service;
   std::shared_ptr<EntityTransactionService::Stub> entity_transaction_service;
 
 public:
-  explicit CaosDBConnection(
-    const std::shared_ptr<CaosDBConnectionConfig> &config);
+  explicit CaosDBConnection(const CaosDBConnectionConfig &config);
   friend auto operator<<(std::ostream &out, const CaosDBConnection &connection)
     -> std::ostream &;
   [[nodiscard]] auto GetVersionInfo() const -> std::unique_ptr<VersionInfo>;
diff --git a/include/caosdb/utils.h b/include/caosdb/utility.h
similarity index 98%
rename from include/caosdb/utils.h
rename to include/caosdb/utility.h
index 78d388a..61e3a31 100644
--- a/include/caosdb/utils.h
+++ b/include/caosdb/utility.h
@@ -32,7 +32,7 @@
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
 
-namespace caosdb::utils {
+namespace caosdb::utility {
 using boost::filesystem::exists;
 using boost::filesystem::ifstream;
 using boost::filesystem::path;
@@ -114,5 +114,5 @@ inline auto load_json_file(const path &json_file) -> value {
   return parser.release();
 }
 
-} // namespace caosdb::utils
+} // namespace caosdb::utility
 #endif
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
index 382364f..2c32888 100644
--- a/include/ccaosdb.h
+++ b/include/ccaosdb.h
@@ -23,8 +23,8 @@ typedef struct {
  * CaosDBConnection is opaque in C.
  */
 typedef struct {
-  void *wrapped_connection_config;
-} caosdb_connection_connection_config;
+  void *wrapped_connection_configuration;
+} caosdb_connection_connection_configuration;
 
 /**
  * A wrapper of the C++ VersionInfo class.
@@ -34,29 +34,80 @@ typedef struct {
  * CaosDBConnection is opaque in C.
  */
 typedef struct {
-  void *wrapped_version_info;
+  int major;
+  int minor;
+  int patch;
+  const char *pre_release;
+  const char *build;
 } caosdb_info_version_info;
 
+typedef struct {
+  void *wrapped_certificate_provider;
+} caosdb_connection_certificate_provider;
+
+typedef struct {
+  void *wrapped_authenticator;
+} caosdb_authentication_authenticator;
+
 /**
  * Return the environment variable of the given name.
  *
  * If the environment variable is not set, return the fall_back instead.
  */
-const char *caosdb_utils_get_env_var(const char *name, const char *fall_back);
+const char *caosdb_utility_get_env_var(const char *name, const char *fall_back);
+
+/**
+ * Create a pem-file certificate provider.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_certificate_provider` to free the wrapped
+ * provider.
+ */
+int caosdb_connection_create_pem_file_certificate_provider(
+  caosdb_connection_certificate_provider *out, const char *path);
 
 /**
- * Create a connection config.
+ * Destructor function for a certificate provider.
+ */
+int caosdb_connection_delete_certificate_provider(
+  caosdb_connection_certificate_provider *provider);
+
+/**
+ * Create a tls-secured connection configuration.
  *
- * The config is needed to instantiate a connection.
+ * The configuration is needed to instantiate a connection.
  *
- * The config is ready to be used but you might want use
- * caosdb_connection_config_add_cacert and
- * caosdb_connection_config_add_plain_authenticator to specify more options of
- * the connection.
+ * Use the destructor function
+ * `caosdb_connection_delete_connection_configuration` to free the wrapped
+ * configuration.
  */
-int caosdb_connection_create_config(caosdb_connection_connection_config *config,
-                                    const char *host, const int port,
-                                    const bool tls);
+int caosdb_connection_create_tls_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port, caosdb_authentication_authenticator *authenticator,
+  caosdb_connection_certificate_provider *provider);
+
+/**
+ * Create a tls-secured connection configuration.
+ *
+ * The configuration is needed to instantiate a connection.
+ *
+ * Use `caosdb_connection_create_tls_connection_configuration` for a
+ * tls-secured connection which also supports authentication.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_connection_configuration` to free the wrapped
+ * configuration.
+ */
+int caosdb_connection_create_insecure_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port);
+
+/**
+ * Destructor function for the caosdb_connection_connection_configuration
+ * struct.
+ */
+int caosdb_connection_delete_connection_configuration(
+  caosdb_connection_connection_configuration *configuration);
 
 /**
  * Add a public certificate of a trusted certificate authority to an
@@ -64,32 +115,52 @@ int caosdb_connection_create_config(caosdb_connection_connection_config *config,
  *
  * @param cacert path to a pem-file.
  */
-int caosdb_connection_config_add_cacert(
-  caosdb_connection_connection_config *config, const char *cacert);
+int caosdb_connection_configuration_add_cacert(
+  caosdb_connection_connection_configuration *configuration,
+  const char *cacert);
 
 /**
- * Add a plain text authenticator to an existing, tls enabled connection
- * configuration.
+ * Create a plain password authenticator.
+ *
+ * Use the destructor function
+ * `caosdb_authentication_delete_authenticator` to free the wrapped
+ * authenticator.
  */
-int caosdb_connection_config_add_plain_authenticator(
-  caosdb_connection_connection_config *config, const char *username,
+int caosdb_authentication_create_plain_password_authenticator(
+  caosdb_authentication_authenticator *out, const char *username,
   const char *password);
 
+/**
+ * Destructor function for the caosdb_authentication_authenticator struct.
+ */
+int caosdb_authentication_delete_authenticator(
+  caosdb_authentication_authenticator *authenticator);
+
 /**
  * Create a connection instance.
  *
  * The connection is needed to create transactions and to initiate any other
  * interaction with a CaosDB server.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_connection` to free the wrapped
+ * connection.
  */
 int caosdb_connection_create_connection(
-  caosdb_connection_connection *connection,
-  const caosdb_connection_connection_config *config);
+  caosdb_connection_connection *out,
+  const caosdb_connection_connection_configuration *configuration);
+
+/**
+ * Destructor function for the caosdb_connection_connection struct.
+ */
+int caosdb_connection_delete_connection(
+  caosdb_connection_connection *connection);
 
 /**
  * Request the version of the server.
  */
 int caosdb_connection_get_version_info(
-  caosdb_info_version_info *version_info,
+  caosdb_info_version_info *out,
   const caosdb_connection_connection *connection);
 
 #ifdef __cplusplus
diff --git a/src/caosdb/authentication.cpp b/src/caosdb/authentication.cpp
index e784982..7bc70de 100644
--- a/src/caosdb/authentication.cpp
+++ b/src/caosdb/authentication.cpp
@@ -28,7 +28,7 @@
 #include "grpcpp/security/credentials.h"    // for MetadataCredentialsFromP...
 
 namespace caosdb::authentication {
-using caosdb::utils::base64_encode;
+using caosdb::utility::base64_encode;
 using grpc::AuthContext;
 using grpc::MetadataCredentialsPlugin;
 using grpc::Status;
@@ -52,7 +52,7 @@ PlainPasswordAuthenticator::PlainPasswordAuthenticator(
   this->basic = "Basic " + base64_encode(username + ":" + password);
 };
 
-auto PlainPasswordAuthenticator::getCallCredentials() const
+auto PlainPasswordAuthenticator::GetCallCredentials() const
   -> std::shared_ptr<grpc::CallCredentials> {
   auto call_creds = grpc::MetadataCredentialsFromPlugin(
     std::unique_ptr<grpc::MetadataCredentialsPlugin>(
diff --git a/src/caosdb/connection.cpp b/src/caosdb/connection.cpp
index 880245b..09a219c 100644
--- a/src/caosdb/connection.cpp
+++ b/src/caosdb/connection.cpp
@@ -33,7 +33,7 @@
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"    // for GeneralInfoService
 #include "caosdb/info/v1alpha1/main.pb.h"         // for GetVersionInfoResp...
 #include "caosdb/transaction.h"                   // for Transaction
-#include "caosdb/utils.h"                         // for load_string_file
+#include "caosdb/utility.h"                       // for load_string_file
 #include "caosdb/info.h"                          // for VersionInfo
 #include "grpcpp/impl/codegen/status_code_enum.h" // for StatusCode, UNAUTH...
 
@@ -47,7 +47,7 @@ using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::info::v1alpha1::GetVersionInfoRequest;
 using caosdb::info::v1alpha1::GetVersionInfoResponse;
 using caosdb::transaction::Transaction;
-using caosdb::utils::load_string_file;
+using caosdb::utility::load_string_file;
 using grpc::InsecureChannelCredentials;
 using grpc::SslCredentials;
 using grpc::SslCredentialsOptions;
@@ -56,7 +56,7 @@ PemFileCACertProvider::PemFileCACertProvider(const std::string &path) {
   this->cacert = load_string_file(path);
 }
 
-auto PemFileCACertProvider::getCACertPem() const -> std::string {
+auto PemFileCACertProvider::GetCACertPem() const -> std::string {
   return this->cacert;
 }
 
@@ -64,7 +64,7 @@ PemCACertProvider::PemCACertProvider(const std::string &cacert) {
   this->cacert = cacert;
 }
 
-auto PemCACertProvider::getCACertPem() const -> std::string {
+auto PemCACertProvider::GetCACertPem() const -> std::string {
   return this->cacert;
 }
 
@@ -74,15 +74,15 @@ CaosDBConnectionConfig::CaosDBConnectionConfig(const std::string &host,
   this->port = port;
 }
 
-auto CaosDBConnectionConfig::getHost() const -> std::string {
+auto CaosDBConnectionConfig::GetHost() const -> std::string {
   return this->host;
 }
 
-auto CaosDBConnectionConfig::getPort() const -> int { return this->port; }
+auto CaosDBConnectionConfig::GetPort() const -> int { return this->port; }
 
 auto operator<<(std::ostream &out, const CaosDBConnectionConfig &config)
   -> std::ostream & {
-  out << config.toString();
+  out << config.ToString();
   return out;
 }
 
@@ -92,64 +92,73 @@ InsecureCaosDBConnectionConfig::InsecureCaosDBConnectionConfig(
   this->credentials = InsecureChannelCredentials();
 }
 
-auto InsecureCaosDBConnectionConfig::getChannelCredentials() const
+auto InsecureCaosDBConnectionConfig::GetChannelCredentials() const
   -> std::shared_ptr<ChannelCredentials> {
   return this->credentials;
 }
 
-auto InsecureCaosDBConnectionConfig::toString() const -> std::string {
-  return "InsecureCaosDBConnectionConfig(" + this->getHost() + "," +
-         std::to_string(this->getPort()) + ")";
+auto InsecureCaosDBConnectionConfig::ToString() const -> std::string {
+  return "InsecureCaosDBConnectionConfig(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + ")";
+}
+
+SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(const std::string &host,
+                                                     int port)
+  : CaosDBConnectionConfig(host, port) {
+  SslCredentialsOptions options;
+  this->credentials = SslCredentials(options);
 }
 
 SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(
-  const std::string &host, int port,
-  const std::shared_ptr<CACertificateProvider> &cacert)
+  const std::string &host, int port, const CACertificateProvider &cacert)
   : CaosDBConnectionConfig(host, port) {
   SslCredentialsOptions options;
-  options.pem_root_certs = cacert->getCACertPem();
-  this->cacert = cacert->getCACertPem();
+  options.pem_root_certs = cacert.GetCACertPem();
   this->credentials = SslCredentials(options);
 }
 
 SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(
-  const std::string &host, int port,
-  const std::shared_ptr<CACertificateProvider> &cacert,
-  const std::shared_ptr<Authenticator> &authenticator)
+  const std::string &host, int port, const Authenticator &authenticator)
+  : CaosDBConnectionConfig(host, port) {
+
+  SslCredentialsOptions options;
+  this->credentials = grpc::CompositeChannelCredentials(
+    SslCredentials(options), authenticator.GetCallCredentials());
+}
+
+SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(
+  const std::string &host, int port, const CACertificateProvider &cacert,
+  const Authenticator &authenticator)
   : CaosDBConnectionConfig(host, port) {
 
   SslCredentialsOptions options;
-  options.pem_root_certs = cacert->getCACertPem();
-  this->cacert = cacert->getCACertPem();
+  options.pem_root_certs = cacert.GetCACertPem();
   this->credentials = grpc::CompositeChannelCredentials(
-    SslCredentials(options), authenticator->getCallCredentials());
+    SslCredentials(options), authenticator.GetCallCredentials());
 }
 
-auto SslCaosDBConnectionConfig::getChannelCredentials() const
+auto SslCaosDBConnectionConfig::GetChannelCredentials() const
   -> std::shared_ptr<ChannelCredentials> {
   return this->credentials;
 }
 
-auto SslCaosDBConnectionConfig::toString() const -> std::string {
-  return "SslCaosDBConnectionConfig(" + this->getHost() + "," +
-         std::to_string(this->getPort()) + "," + this->cacert + ")";
+auto SslCaosDBConnectionConfig::ToString() const -> std::string {
+  return "SslCaosDBConnectionConfig(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + "," + this->cacert + ")";
 }
 
-CaosDBConnection::CaosDBConnection(
-  const std::shared_ptr<CaosDBConnectionConfig> &config) {
-  this->config = config;
+CaosDBConnection::CaosDBConnection(const CaosDBConnectionConfig &config) {
   const std::string target =
-    this->config->getHost() + ":" + std::to_string(this->config->getPort());
-  this->channel =
-    grpc::CreateChannel(target, this->config->getChannelCredentials());
+    config.GetHost() + ":" + std::to_string(config.GetPort());
+  this->channel = grpc::CreateChannel(target, config.GetChannelCredentials());
   this->general_info_service = GeneralInfoService::NewStub(this->channel);
   this->entity_transaction_service =
     std::make_shared<EntityTransactionService::Stub>(this->channel);
 }
 
-auto operator<<(std::ostream &out, const CaosDBConnection &connection)
+auto operator<<(std::ostream &out, const CaosDBConnection & /*connection*/)
   -> std::ostream & {
-  out << "CaosDBConnection(" << *(connection.config) << ")";
+  out << "CaosDBConnection()";
   return out;
 }
 
diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp
index 28ea242..076b23e 100644
--- a/src/ccaosdb.cpp
+++ b/src/ccaosdb.cpp
@@ -1,25 +1,129 @@
-#include "caosdb/utils.h"
+#include "caosdb/utility.h"
 #include "caosdb/constants.h"
+#include "caosdb/connection.h"
 #include "ccaosdb.h"
 
 extern "C" {
-const int LIBCAOSDB_VERSION_MINOR = caosdb::LIBCAOSDB_VERSION_MAJOR;
-const int LIBCAOSDB_VERSION_MAJOR = caosdb::LIBCAOSDB_VERSION_MINOR;
-const int LIBCAOSDB_VERSION_PATCH = caosdb::LIBCAOSDB_VERSION_PATCH;
 
-const char *caosdb_utils_get_env_var(const char *name, const char *fall_back) {
-  return caosdb::utils::get_env_var(name, fall_back);
+const char *caosdb_utility_get_env_var(const char *name,
+                                       const char *fall_back) {
+  return caosdb::utility::get_env_var(name, fall_back);
 }
 
-// int caosdb_connection_create_config(caosdb_connection_config *config, const
-// char *host, const int port, const bool tls);
+int caosdb_connection_create_pem_file_certificate_provider(
+  caosdb_connection_certificate_provider *out, const char *path) {
+  out->wrapped_certificate_provider =
+    new caosdb::connection::PemFileCACertProvider(std::string(path));
+  return 0;
+}
+
+int caosdb_connection_delete_certificate_provider(
+  caosdb_connection_certificate_provider *provider) {
+  delete static_cast<caosdb::connection::CACertificateProvider *>(
+    provider->wrapped_certificate_provider);
+  return 0;
+}
+
+int caosdb_authentication_create_plain_password_authenticator(
+  caosdb_authentication_authenticator *out, const char *username,
+  const char *password) {
+  out->wrapped_authenticator =
+    new caosdb::authentication::PlainPasswordAuthenticator(
+      std::string(username), std::string(password));
+  return 0;
+}
+
+int caosdb_authentication_delete_authenticator(
+  caosdb_authentication_authenticator *authenticator) {
+  delete static_cast<caosdb::authentication::Authenticator *>(
+    authenticator->wrapped_authenticator);
+  return 0;
+}
+
+int caosdb_connection_create_tls_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port, caosdb_authentication_authenticator *authenticator,
+  caosdb_connection_certificate_provider *provider) {
+
+  auto host_str = std::string(host);
+  if (authenticator != nullptr && provider != nullptr) {
+    auto wrapped_provider =
+      static_cast<caosdb::connection::CACertificateProvider *>(
+        provider->wrapped_certificate_provider);
+    auto wrapped_authenticator =
+      static_cast<caosdb::authentication::Authenticator *>(
+        authenticator->wrapped_authenticator);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::SslCaosDBConnectionConfig(
+        host_str, port, *wrapped_provider, *wrapped_authenticator);
+  } else if (authenticator != nullptr) {
+    auto wrapped_authenticator =
+      static_cast<caosdb::authentication::Authenticator *>(
+        authenticator->wrapped_authenticator);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::SslCaosDBConnectionConfig(host_str, port,
+                                                        *wrapped_authenticator);
+  } else if (provider != nullptr) {
+    auto wrapped_provider =
+      static_cast<caosdb::connection::CACertificateProvider *>(
+        provider->wrapped_certificate_provider);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::SslCaosDBConnectionConfig(host_str, port,
+                                                        *wrapped_provider);
+  } else {
+    out->wrapped_connection_configuration =
+      new caosdb::connection::SslCaosDBConnectionConfig(host_str, port);
+  }
+  return 0;
+}
 
-// int caosdb_connection_config_add_cacert(caosdb_connection_config *config,
-// const char *cacert);
+int caosdb_connection_create_insecure_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port) {
+  out->wrapped_connection_configuration =
+    new caosdb::connection::InsecureCaosDBConnectionConfig(host, port);
+  return 0;
+}
+
+int caosdb_connection_delete_connection_configuration(
+  caosdb_connection_connection_configuration *configuration) {
+  delete static_cast<caosdb::connection::CaosDBConnectionConfig *>(
+    configuration->wrapped_connection_configuration);
+  return 0;
+}
+
+int caosdb_connection_create_connection(
+  caosdb_connection_connection *out,
+  const caosdb_connection_connection_configuration *configuration) {
+  caosdb::connection::CaosDBConnectionConfig *config =
+    static_cast<caosdb::connection::CaosDBConnectionConfig *>(
+      configuration->wrapped_connection_configuration);
+  out->wrapped_connection = new caosdb::connection::CaosDBConnection(*config);
+  return 0;
+}
 
-// int caosdb_connection_config_add_plain_authenticator(caosdb_connection_config
-// *config, const char *username, const char *password);
+int caosdb_connection_delete_connection(
+  caosdb_connection_connection *connection) {
+  delete static_cast<caosdb::connection::CaosDBConnection *>(
+    connection->wrapped_connection);
+  return 0;
+}
+
+int caosdb_connection_get_version_info(
+  caosdb_info_version_info *out,
+  const caosdb_connection_connection *connection) {
+  auto *wrapped_connection =
+    static_cast<caosdb::connection::CaosDBConnection *>(
+      connection->wrapped_connection);
+  auto version_info = wrapped_connection->GetVersionInfo();
+
+  out->major = (int)version_info->GetMajor();
+  out->minor = (int)version_info->GetMinor();
+  out->patch = (int)version_info->GetPatch();
 
-// int caosdb_connection_create_connection(caosdb_connection *connection, const
-// caosdb_connection_config *config);
+  out->pre_release = version_info->GetPreRelease().c_str();
+  out->build = version_info->GetBuild().c_str();
+
+  return 0;
+}
 }
diff --git a/src/ccaosdbcli.c b/src/ccaosdbcli.c
index 3ba70bc..2388130 100644
--- a/src/ccaosdbcli.c
+++ b/src/ccaosdbcli.c
@@ -9,12 +9,44 @@ int main(void) {
     LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_VERSION_PATCH);
 
   const char *host =
-    caosdb_utils_get_env_var("CAOSDB_SERVER_HOST", "localhost");
-  const char *port_str = caosdb_utils_get_env_var("CAOSDB_SERVER_HOST", "8443");
+    caosdb_utility_get_env_var("CAOSDB_SERVER_HOST", "localhost");
+  const char *port_str =
+    caosdb_utility_get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443");
   char *end = NULL;
   const int port = (int)strtol(port_str, &end, 10);
 
-  printf("Connecting to host: %s:%d\n", host, port);
+  const char *cacert = caosdb_utility_get_env_var("CAOSDB_SERVER_CERT", "");
+  const char *username = caosdb_utility_get_env_var("CAOSDB_USER", "admin");
+  const char *password =
+    caosdb_utility_get_env_var("CAOSDB_PASSWORD", "caosdb");
+
+  printf("Connecting to %s:%d as %s: ...", host, port, username);
+
+  caosdb_connection_certificate_provider provider;
+  caosdb_connection_create_pem_file_certificate_provider(&provider, cacert);
+
+  caosdb_authentication_authenticator authenticator;
+  caosdb_authentication_create_plain_password_authenticator(&authenticator,
+                                                            username, password);
+
+  caosdb_connection_connection_configuration configuration;
+  caosdb_connection_create_tls_connection_configuration(
+    &configuration, host, port, &authenticator, &provider);
+
+  caosdb_connection_connection connection;
+  caosdb_connection_create_connection(&connection, &configuration);
+
+  caosdb_info_version_info version_info;
+  caosdb_connection_get_version_info(&version_info, &connection);
+
+  printf("\b\b\bSuccess!\nServer version: %d.%d.%d-%s-%s\n", version_info.major,
+         version_info.minor, version_info.patch, version_info.pre_release,
+         version_info.build);
+
+  caosdb_connection_delete_connection(&connection);
+  caosdb_connection_delete_connection_configuration(&configuration);
+  caosdb_connection_delete_certificate_provider(&provider);
+  caosdb_authentication_delete_authenticator(&authenticator);
 
   return 0;
 }
diff --git a/src/cxxcaosdbcli.cpp b/src/cxxcaosdbcli.cpp
index 443c071..af83b40 100644
--- a/src/cxxcaosdbcli.cpp
+++ b/src/cxxcaosdbcli.cpp
@@ -27,7 +27,7 @@
 #include "caosdb/constants.h"
 #include "caosdb/connection.h"
 #include "caosdb/authentication.h"
-#include "caosdb/utils.h"
+#include "caosdb/utility.h"
 #include "caosdb/info.h"
 #include "caosdb/entity.h"      // for Entity, EntityID
 #include "caosdb/transaction.h" // for Transaction, UniqueResult
@@ -41,24 +41,22 @@ auto main() -> int {
             << std::endl;
 
   const auto pem_file =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_CERT", std::string());
+    caosdb::utility::get_env_var("CAOSDB_SERVER_CERT", std::string());
   const auto *const host =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_HOST", "localhost");
+    caosdb::utility::get_env_var("CAOSDB_SERVER_HOST", "localhost");
   const auto *const port_str =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443");
+    caosdb::utility::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443");
   const auto port = std::stoi(port_str);
-  const auto *const user = caosdb::utils::get_env_var("CAOSDB_USER", "admin");
+  const auto *const user = caosdb::utility::get_env_var("CAOSDB_USER", "admin");
   const auto *const password =
-    caosdb::utils::get_env_var("CAOSDB_PASSWORD", "caosdb");
+    caosdb::utility::get_env_var("CAOSDB_PASSWORD", "caosdb");
 
   // setup the connection
   auto auth =
-    std::make_shared<caosdb::authentication::PlainPasswordAuthenticator>(
-      user, password);
-  auto cacert =
-    std::make_shared<caosdb::connection::PemFileCACertProvider>(pem_file);
-  auto config = std::make_shared<caosdb::connection::SslCaosDBConnectionConfig>(
-    host, port, cacert, auth);
+    caosdb::authentication::PlainPasswordAuthenticator(user, password);
+  auto cacert = caosdb::connection::PemFileCACertProvider(pem_file);
+  auto config =
+    caosdb::connection::SslCaosDBConnectionConfig(host, port, cacert, auth);
   caosdb::connection::CaosDBConnection connection(config);
 
   // get version info of the server
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0273078..5d073cd 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -23,7 +23,7 @@ set(test_cases
     test_connection
     test_info
     test_transaction
-    test_utils
+    test_utility
     test_ccaosdb
     )
 
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
index bdec5b6..70f0fb3 100644
--- a/test/test_ccaosdb.cpp
+++ b/test/test_ccaosdb.cpp
@@ -24,10 +24,10 @@
 #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
 #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
 #include <string>                  // for allocator
-#include "ccaosdb.h"               // for caosdb_utils_get_env_var
+#include "ccaosdb.h"               // for caosdb_utility_get_env_var
 
 TEST(test_ccaosdb, test_get_env_var) {
   const char *const some_var =
-    caosdb_utils_get_env_var("SOME_ENV_VAR", "fall-back");
+    caosdb_utility_get_env_var("SOME_ENV_VAR", "fall-back");
   EXPECT_EQ("fall-back", some_var);
 }
diff --git a/test/test_connection.cpp b/test/test_connection.cpp
index 34abc0e..c2ed89e 100644
--- a/test/test_connection.cpp
+++ b/test/test_connection.cpp
@@ -20,11 +20,12 @@
  *
  */
 
-#include <gtest/gtest-message.h>   // for Message
-#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
-#include <memory>                  // for allocator, operator!=, shared_ptr
-#include "caosdb/connection.h"     // for PemCACertProvider, InsecureCaosDB...
-#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, EXPECT_EQ
+#include <gtest/gtest-message.h>         // for Message
+#include <gtest/gtest-test-part.h>       // for TestPartResult, SuiteApiRes...
+#include <memory>                        // for allocator, operator!=, shar...
+#include "caosdb/connection.h"           // for PemCACertProvider, Insecure...
+#include "grpcpp/security/credentials.h" // for ChannelCredentials
+#include "gtest/gtest_pred_impl.h"       // for Test, AssertionResult, EXPE...
 
 namespace caosdb::connection {
 
diff --git a/test/test_utils.cpp b/test/test_utility.cpp
similarity index 91%
rename from test/test_utils.cpp
rename to test/test_utility.cpp
index c8b5dc8..400e0a6 100644
--- a/test/test_utils.cpp
+++ b/test/test_utility.cpp
@@ -27,14 +27,14 @@
 #include <string>                             // for allocator, string, ope...
 #include "boost/json/object.hpp"              // for object
 #include "boost/json/value.hpp"               // for value
-#include "caosdb/utils.h"                     // for base64_encode, load_js...
+#include "caosdb/utility.h"                   // for base64_encode, load_js...
 #include "caosdb_test_utility.h"              // for TEST_DATA_DIR
 #include "gmock/gmock-matchers.h"             // for ElementsAre, EXPECT_THAT
 
-namespace caosdb::utils {
+namespace caosdb::utility {
 using ::testing::ElementsAre;
 
-TEST(test_utils, base64_encode) {
+TEST(test_utility, base64_encode) {
   auto test_plain = std::string("admin:caosdb");
   auto test_encoded = std::string("YWRtaW46Y2Fvc2Ri");
   EXPECT_EQ(12, test_plain.size());
@@ -42,7 +42,7 @@ TEST(test_utils, base64_encode) {
   EXPECT_EQ(test_encoded, base64_encode(test_plain));
 }
 
-TEST(test_utils, test_load_json_file) {
+TEST(test_utility, test_load_json_file) {
   auto json = load_json_file(TEST_DATA_DIR + "/test.json").as_object();
 
   EXPECT_EQ(json["it"], "tests");
@@ -53,4 +53,4 @@ TEST(test_utils, test_load_json_file) {
   EXPECT_THAT(sub["see?"].as_array(), ElementsAre(true, false));
 }
 
-} // namespace caosdb::utils
+} // namespace caosdb::utility
-- 
GitLab