1 /// Provides connection parameters for memgraph.
2 module memgraph.params;
3 
4 import std.string, std.conv;
5 
6 import memgraph.mgclient;
7 
8 /// An object containing connection parameters for `Client.connect(Params)`.
9 struct Params {
10   /// DNS resolvable name of host to connect to. Either one of host or
11   /// address parameters must be specified (defaults to `127.0.0.1`).
12   string host = "127.0.0.1";
13   /// Port number to connect to at the server host (defaults to 7687).
14   ushort port = 7687;
15   /// Numeric IP address of host to connect to. This should be in the
16   /// standard IPv4 address format. You can also use IPv6 if your machine
17   /// supports it. Either one of host or address parameters must be
18   /// specified.
19   string address;
20   /// Username, if authentication is required.
21   string username;
22   /// Password to be used if the server demands password authentication.
23   string password;
24   /// This option determines whether a secure connection will be negotiated
25   /// with the server. There are 2 possible values:
26   /// - `MG_SSLMODE_DISABLE`
27   ///   Only try a non-SSL connection (default).
28   /// - `MG_SSLMODE_REQUIRE`
29   ///   Only try an SSL connection.
30   mg_sslmode sslMode = mg_sslmode.MG_SSLMODE_DISABLE;
31   /// This parameter specifies the file name of the client SSL certificate.
32   /// It is ignored in case an SSL connection is not made.
33   string sslCert;
34   /// This parameter specifies the location of the secret key used for the
35   /// client certificate. This parameter is ignored in case an SSL connection
36   /// is not made.
37   string sslKey;
38   /// Useragent used when connecting to memgraph, defaults to
39   /// Alternate name and version of the client to send to server. Default is
40   /// "memgraph-d/major.minor.patch".
41   string userAgent;
42   /// A pointer to a function of prototype `mg_trust_callback_type`:
43   ///   int trust_callback(const char *hostname, const char *ip_address,
44   ///                      const char *key_type, const char *fingerprint,
45   ///                      void *trust_data);
46   ///
47   /// After performing the SSL handshake, `mg_connect` will call this
48   /// function providing the hostname, IP address, public key type and
49   /// fingerprint and user provided data. If the function returns a non-zero
50   /// value, SSL connection will be immediately terminated. This can be used
51   /// to implement TOFU (trust on first use) mechanism.
52   /// It might happen that hostname can not be determined, in that case the
53   /// trust callback will be called with hostname="undefined".
54   mg_trust_callback_type sslTrustCallback;
55   /// Additional data that will be provided to the sslTrustCallback function.
56   void *sslTrustData;
57 
58   /// Destructor, destroys the internal session parameters.
59   @nogc ~this() {
60     if (ptr_)
61       mg_session_params_destroy(ptr_);
62   }
63 
64   /// Post-blit takes care of copied pointer so that the new instance
65   /// will create its own `mg_session_params` if required.
66   @nogc this(this) {
67     ptr_ = null;
68   }
69 
70   /// Compares the struct members that go into the session parameters.
71   /// Return: `true` if both are identical, `false` otherwise.
72   @nogc auto opEquals(const ref Params rhs) const {
73     return host == rhs.host && port == rhs.port && address == rhs.address &&
74            username == rhs.username && password == rhs.password &&
75            sslMode == rhs.sslMode && sslCert == rhs.sslCert && sslKey == rhs.sslKey &&
76            userAgent == rhs.userAgent &&
77            sslTrustCallback == rhs.sslTrustCallback && sslTrustData == rhs.sslTrustData;
78   }
79 
80 package:
81   /// Creates a new internal `mg_session_params` instance on the first call and
82   /// returns a pointer to it. Transfers the session parameters from the struct
83   /// members into the session using the appropriate set function calls.
84   auto ptr() {
85     if (!ptr_)
86       ptr_ = mg_session_params_make();
87     if (host.length)
88       mg_session_params_set_host(ptr_, toStringz(host));
89     if (address.length)
90       mg_session_params_set_address(ptr_, toStringz(address));
91     if (port)
92       mg_session_params_set_port(ptr_, port);
93     if (username.length)
94       mg_session_params_set_username(ptr_, toStringz(username));
95     if (password.length)
96       mg_session_params_set_password(ptr_, toStringz(password));
97 
98     if (!userAgent.length)
99       userAgent = to!string("memgraph-d/" ~ fromStringz(mg_client_version()));
100     mg_session_params_set_user_agent(ptr_, toStringz(userAgent));
101 
102     mg_session_params_set_sslmode(ptr_, sslMode);
103     if (sslCert.length)
104       mg_session_params_set_sslcert(ptr_, toStringz(sslCert));
105     if (sslKey.length)
106       mg_session_params_set_sslkey(ptr_, toStringz(sslKey));
107 
108     if (sslTrustCallback)
109       mg_session_params_set_trust_callback(ptr_, sslTrustCallback);
110     if (sslTrustData)
111       mg_session_params_set_trust_data(ptr_, sslTrustData);
112 
113     return ptr_;
114   }
115 
116 private:
117   /// Pointer to private `mg_session_params` instance that
118   /// contains all parameters for this `Params` structure.
119   mg_session_params *ptr_;
120 } // struct Params
121 
122 unittest {
123   Params p;
124 
125   // Check defaults.
126   assert(p.host == "127.0.0.1");
127   assert(p.port == 7687);
128   assert(p.sslMode == mg_sslmode.MG_SSLMODE_DISABLE);
129 
130   // Set some parameters.
131   p.address = "127.0.0.1";
132   p.username = "sini";
133   p.password = "whatever";
134 
135   p.sslCert = "someCertFile";
136   p.sslKey = "someKeyFile";
137 
138   ubyte[] trustData = cast(ubyte[])"trustData";
139   p.sslTrustData = cast(void*)trustData;
140   p.sslTrustCallback = (hostname, ip_address, key_type, fingerprint, trust_data) { return 0; };
141 
142   // This call will internally create a `mg_session_params` instance
143   // and return a pointer to it.
144   assert(p.ptr != null);
145 
146   // Make sure the internal pointer reflects the above parameter settings.
147   auto ptr = p.ptr;
148   assert(p.host == fromStringz(mg_session_params_get_host(ptr)));
149   assert(p.port == mg_session_params_get_port(ptr));
150   assert(p.sslMode == mg_session_params_get_sslmode(ptr));
151   assert(p.address == fromStringz(mg_session_params_get_address(ptr)));
152   assert(p.username == fromStringz(mg_session_params_get_username(ptr)));
153   assert(p.password == fromStringz(mg_session_params_get_password(ptr)));
154   assert(p.sslCert == fromStringz(mg_session_params_get_sslcert(ptr)));
155   assert(p.sslKey == fromStringz(mg_session_params_get_sslkey(ptr)));
156   assert(fromStringz(cast(char*)p.sslTrustData) == fromStringz(cast(char*)mg_session_params_get_trust_data(ptr)));
157   assert(p.sslTrustCallback == mg_session_params_get_trust_callback(ptr));
158 
159   // Exercise post-blit and make sure returned pointers point to different `mg_session_params`.
160   Params p2 = p;
161   assert(p2.ptr != null);
162   assert(p2.ptr != p.ptr);
163 
164   // Both parameter structs should contain the same parameters and should thus be equal.
165   assert(p2 == p);
166 }