1 /*
2  * Copyright (C) 2003 Akira TAGOH <tagoh@gnome-db.org>
3  * Copyright (C) 2003 German Poo-Caaman~o <gpoo@ubiobio.cl>
4  * Copyright (C) 2003 Gonzalo Paniagua Javier <gonzalo@gnome-db.org>
5  * Copyright (C) 2003 Rodrigo Moya <rodrigo@gnome-db.org>
6  * Copyright (C) 2004 Julio M. Merino Vidal <jmmv@menta.net>
7  * Copyright (C) 2004 Jürg Billeter <j@bitron.ch>
8  * Copyright (C) 2004 Szalai Ferenc <szferi@einstein.ki.iif.hu>
9  * Copyright (C) 2005 - 2012 Vivien Malerba <malerba@gnome-db.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24  * Boston, MA  02110-1301, USA.
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <glib/gi18n-lib.h>
30 #include <virtual/gda-ldap-connection.h>
31 #include <libgda/gda-connection-private.h>
32 #include <libgda/gda-data-model-iter.h>
33 #include <libgda/gda-util.h>
34 #include <libgda/gda-data-model-array.h>
35 #include <libgda/sql-parser/gda-sql-parser.h>
36 #include "gda-ldap.h"
37 #include "gda-ldap-provider.h"
38 #include "gdaprov-data-model-ldap.h"
39 #include "gda-ldap-util.h"
40 
41 static void gda_ldap_provider_class_init (GdaLdapProviderClass *klass);
42 static void gda_ldap_provider_init       (GdaLdapProvider *provider,
43 					  GdaLdapProviderClass *klass);
44 static void gda_ldap_provider_finalize   (GObject *object);
45 
46 static const gchar *gda_ldap_provider_get_name (GdaServerProvider *provider);
47 static const gchar *gda_ldap_provider_get_version (GdaServerProvider *provider);
48 static GdaConnection *gda_ldap_provider_create_connection (GdaServerProvider *provider);
49 static gboolean gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
50 						   GdaQuarkList *params, GdaQuarkList *auth,
51 						   guint *task_id, GdaServerProviderAsyncCallback async_cb, gpointer cb_data);
52 static GObject *gda_ldap_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
53 						     GdaStatement *stmt, GdaSet *params,
54 						     GdaStatementModelUsage model_usage,
55 						     GType *col_types, GdaSet **last_inserted_row,
56 						     guint *task_id, GdaServerProviderExecCallback async_cb,
57 						     gpointer cb_data, GError **error);
58 static const gchar *gda_ldap_provider_get_server_version (GdaServerProvider *provider,
59 							  GdaConnection *cnc);
60 static const gchar *gda_ldap_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc);
61 
62 static GObjectClass *parent_class = NULL;
63 
64 /*
65  * private connection data destroy
66  */
67 static void gda_ldap_free_cnc_data (LdapConnectionData *cdata);
68 
69 /*
70  * GdaLdapProvider class implementation
71  */
72 static void
gda_ldap_provider_class_init(GdaLdapProviderClass * klass)73 gda_ldap_provider_class_init (GdaLdapProviderClass *klass)
74 {
75 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
76 	GdaServerProviderClass *provider_class = GDA_SERVER_PROVIDER_CLASS (klass);
77 
78 	parent_class = g_type_class_peek_parent (klass);
79 
80 	object_class->finalize = gda_ldap_provider_finalize;
81 	provider_class->create_connection = gda_ldap_provider_create_connection;
82 
83 	provider_class->get_name = gda_ldap_provider_get_name;
84 	provider_class->get_version = gda_ldap_provider_get_version;
85 	provider_class->open_connection = gda_ldap_provider_open_connection;
86 	provider_class->get_server_version = gda_ldap_provider_get_server_version;
87 	provider_class->get_database = gda_ldap_provider_get_database;
88 	provider_class->statement_execute = gda_ldap_provider_statement_execute;
89 }
90 
91 static void
gda_ldap_provider_init(G_GNUC_UNUSED GdaLdapProvider * pg_prv,G_GNUC_UNUSED GdaLdapProviderClass * klass)92 gda_ldap_provider_init (G_GNUC_UNUSED GdaLdapProvider *pg_prv,
93 			G_GNUC_UNUSED GdaLdapProviderClass *klass)
94 {
95 	/* nothing specific there */
96 }
97 
98 static void
gda_ldap_provider_finalize(GObject * object)99 gda_ldap_provider_finalize (GObject *object)
100 {
101 	GdaLdapProvider *pg_prv = (GdaLdapProvider *) object;
102 
103 	g_return_if_fail (GDA_IS_LDAP_PROVIDER (pg_prv));
104 
105 	/* chain to parent class */
106 	parent_class->finalize(object);
107 }
108 
109 GType
gda_ldap_provider_get_type(void)110 gda_ldap_provider_get_type (void)
111 {
112 	static GType type = 0;
113 
114 	if (G_UNLIKELY (type == 0)) {
115 		static GMutex registering;
116 		static GTypeInfo info = {
117 			sizeof (GdaLdapProviderClass),
118 			(GBaseInitFunc) NULL,
119 			(GBaseFinalizeFunc) NULL,
120 			(GClassInitFunc) gda_ldap_provider_class_init,
121 			NULL, NULL,
122 			sizeof (GdaLdapProvider),
123 			0,
124 			(GInstanceInitFunc) gda_ldap_provider_init,
125 			0
126 		};
127 		g_mutex_lock (&registering);
128 		if (type == 0)
129 			type = g_type_register_static (GDA_TYPE_VPROVIDER_DATA_MODEL, "GdaLdapProvider", &info, 0);
130 		g_mutex_unlock (&registering);
131 	}
132 
133 	return type;
134 }
135 
136 /*
137  * Get provider name request
138  */
139 static const gchar *
gda_ldap_provider_get_name(G_GNUC_UNUSED GdaServerProvider * provider)140 gda_ldap_provider_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
141 {
142 	return LDAP_PROVIDER_NAME;
143 }
144 
145 /*
146  * Get version request
147  */
148 static const gchar *
gda_ldap_provider_get_version(G_GNUC_UNUSED GdaServerProvider * provider)149 gda_ldap_provider_get_version (G_GNUC_UNUSED GdaServerProvider *provider)
150 {
151 	return PACKAGE_VERSION;
152 }
153 
154 static GdaConnection *
gda_ldap_provider_create_connection(GdaServerProvider * provider)155 gda_ldap_provider_create_connection (GdaServerProvider *provider)
156 {
157 	GdaConnection *cnc;
158         g_return_val_if_fail (GDA_IS_LDAP_PROVIDER (provider), NULL);
159 
160         cnc = g_object_new (GDA_TYPE_LDAP_CONNECTION, "provider", provider, NULL);
161 
162         return cnc;
163 }
164 
165 /*
166  * compute cache file's absolute name
167  */
168 static gchar *
compute_data_file_name(GdaQuarkList * params,gboolean is_cache,const gchar * data_type)169 compute_data_file_name (GdaQuarkList *params, gboolean is_cache, const gchar *data_type)
170 {
171 	/* real cache file name */
172 	GString *string;
173 	gchar *cfile, *evalue;
174 	const gchar *base_dn;
175 	const gchar *host;
176 	const gchar *require_ssl;
177 	const gchar *port;
178 	gint rport;
179 	gboolean use_ssl;
180 
181         base_dn = gda_quark_list_find (params, "DB_NAME");
182 	host = gda_quark_list_find (params, "HOST");
183 	if (!host)
184 		host = "127.0.0.1";
185         port = gda_quark_list_find (params, "PORT");
186         require_ssl = gda_quark_list_find (params, "USE_SSL");
187 	use_ssl = (require_ssl && ((*require_ssl == 't') || (*require_ssl == 'T'))) ? TRUE : FALSE;
188 	if (port && *port)
189 		rport = atoi (port);
190 	else {
191 		if (use_ssl)
192 			rport = LDAPS_PORT;
193 		else
194 			rport = LDAP_PORT;
195 	}
196 	string = g_string_new ("");
197 	evalue = gda_rfc1738_encode (host);
198 	g_string_append_printf (string, ",=%s", evalue);
199 	g_free (evalue);
200 	g_string_append_printf (string, ";PORT=%d", rport);
201 	if (base_dn) {
202 		evalue = gda_rfc1738_encode (base_dn);
203 		g_string_append_printf (string, ";BASE_DN,=%s", evalue);
204 		g_free (evalue);
205 	}
206 	evalue = g_compute_checksum_for_string (G_CHECKSUM_SHA1, string->str, -1);
207 	g_string_free (string, TRUE);
208 	if (is_cache)
209 		cfile = g_strdup_printf ("%s_%s", evalue, data_type);
210 	else
211 		cfile = g_strdup_printf ("ldap-%s.%s", evalue, data_type);
212 	g_free (evalue);
213 
214 	gchar *fname;
215 	if (is_cache)
216 		fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (),
217 				      "libgda", "ldap", cfile, NULL);
218 	else
219 		fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
220 				      "libgda", cfile, NULL);
221 
222 	g_free (cfile);
223 	return fname;
224 }
225 
226 typedef struct {
227 	gchar *filter_format;
228 	gchar *attribute;
229 } LdapAuthMapping;
230 
231 LdapAuthMapping mappings[] = {
232 	{"(&(uid=%s)(objectclass=inetOrgPerson))", "uid"},
233 	{"(sAMAccountName=%s)", "sAMAccountName"}, /* Active Directory */
234 };
235 
236 /*
237  * Using @url and @username, performs the following tasks:
238  * - bind to the LDAP server anonymously
239  * - search the directory to identify the entry for the provided user name,
240  *   filter: (&(uid=##uid)(objectclass=inetOrgPerson))
241  * - if one and only one entry is returned, get the DN of the entry and check that the UID is correct
242  *
243  * If all the steps are right, it returns the DN of the identified entry as a new string.
244  */
245 static gchar *
fetch_user_dn(const gchar * url,const gchar * base,const gchar * username,LdapAuthMapping * mapping)246 fetch_user_dn (const gchar *url, const gchar *base, const gchar *username, LdapAuthMapping *mapping)
247 {
248 	LDAP *ld;
249 	int res;
250 	int version = LDAP_VERSION3;
251 	gchar *dn = NULL;
252 	LDAPMessage *msg = NULL;
253 
254 	if (! username)
255 		return NULL;
256 
257 	res = ldap_initialize (&ld, url);
258 	if (res != LDAP_SUCCESS)
259 		return NULL;
260 
261 	res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
262         if (res != LDAP_SUCCESS) {
263 		if (res == LDAP_PROTOCOL_ERROR) {
264 			version = LDAP_VERSION2;
265 			res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
266 		}
267 		if (res != LDAP_SUCCESS)
268 			goto out;
269         }
270 
271 	struct berval cred;
272         memset (&cred, 0, sizeof (cred));
273 	res = ldap_sasl_bind_s (ld, NULL, NULL, &cred, NULL, NULL, NULL);
274 	if (res != LDAP_SUCCESS)
275 		goto out;
276 
277 	gchar *filter;
278 	gchar *attributes[] = {NULL, NULL};
279 	attributes[0] = mapping->attribute;
280 	filter = g_strdup_printf (mapping->filter_format, username);
281 	res = ldap_search_ext_s (ld, base, LDAP_SCOPE_SUBTREE,
282 				 filter, attributes, 0,
283 				 NULL, NULL, NULL, 2, &msg);
284 	g_free (filter);
285 	if (res != LDAP_SUCCESS)
286 		goto out;
287 
288 	LDAPMessage *ldap_row;
289 	for (ldap_row = ldap_first_entry (ld, msg);
290 	     ldap_row;
291 	     ldap_row = ldap_next_entry (ld, ldap_row)) {
292 		char *attr, *uid;
293 		attr = ldap_get_dn (ld, ldap_row);
294 		if (attr) {
295 			BerElement* ber;
296 			for (uid = ldap_first_attribute (ld, ldap_row, &ber);
297 			     uid;
298 			     uid = ldap_next_attribute (ld, ldap_row, ber)) {
299 				BerValue **bvals;
300 				bvals = ldap_get_values_len (ld, ldap_row, uid);
301 				if (!bvals || !bvals[0] || bvals[1] || strcmp (bvals[0]->bv_val, username)) {
302 					g_free (dn);
303 					dn = NULL;
304 				}
305 				ldap_value_free_len (bvals);
306 				ldap_memfree (uid);
307 			}
308 
309 			if (dn) {
310 				/* more than 1 entry => unique DN could not be identified */
311 				g_free (dn);
312 				dn = NULL;
313 				goto out;
314 			}
315 
316 			dn = g_strdup (attr);
317 			ldap_memfree (attr);
318 		}
319 	}
320 
321  out:
322 	if (msg)
323 		ldap_msgfree (msg);
324 	ldap_unbind_ext (ld, NULL, NULL);
325 	/*g_print ("Identified DN: [%s]\n", dn);*/
326 	return dn;
327 }
328 
329 /*
330  * Open connection request
331  *
332  * In this function, the following _must_ be done:
333  *   - check for the presence and validify of the parameters required to actually open a connection,
334  *     using @params
335  *   - open the real connection to the database using the parameters previously checked, create one or
336  *     more GdaDataModel objects and declare them to the virtual connection with table names
337  *   - open virtual (SQLite) connection
338  *   - create a LdapConnectionData structure and associate it to @cnc
339  *
340  * Returns: TRUE if no error occurred, or FALSE otherwise (and an ERROR connection event must be added to @cnc)
341  */
342 static gboolean
gda_ldap_provider_open_connection(GdaServerProvider * provider,GdaConnection * cnc,GdaQuarkList * params,GdaQuarkList * auth,G_GNUC_UNUSED guint * task_id,GdaServerProviderAsyncCallback async_cb,G_GNUC_UNUSED gpointer cb_data)343 gda_ldap_provider_open_connection (GdaServerProvider *provider, GdaConnection *cnc,
344 				   GdaQuarkList *params, GdaQuarkList *auth,
345 				   G_GNUC_UNUSED guint *task_id, GdaServerProviderAsyncCallback async_cb,
346 				   G_GNUC_UNUSED gpointer cb_data)
347 {
348 	g_return_val_if_fail (GDA_IS_LDAP_PROVIDER (provider), FALSE);
349 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
350 
351 	/* Don't allow asynchronous connection opening for virtual providers */
352 	if (async_cb) {
353 		gda_connection_add_event_string (cnc, _("Provider does not support asynchronous connection open"));
354                 return FALSE;
355 	}
356 
357 	/* Check for connection parameters */
358 	const gchar *base_dn;
359 	const gchar *host;
360 	const gchar *tmp;
361 	const gchar *port;
362 	const gchar *user = NULL;
363 	gchar *dnuser = NULL;
364         const gchar *pwd = NULL;
365         const gchar *time_limit = NULL;
366         const gchar *size_limit = NULL;
367         const gchar *tls_method = NULL;
368         const gchar *tls_cacert = NULL;
369 	int rtls_method = -1;
370 	gint rport;
371 	gboolean use_ssl, use_cache;
372 
373         base_dn = gda_quark_list_find (params, "DB_NAME");
374         if (!base_dn) {
375                 gda_connection_add_event_string (cnc, "%s",
376                                                  _("The connection string must contain the DB_NAME value"));
377                 return FALSE;
378         }
379 	host = gda_quark_list_find (params, "HOST");
380 	if (!host)
381 		host = "127.0.0.1";
382         port = gda_quark_list_find (params, "PORT");
383         tmp = gda_quark_list_find (params, "USE_SSL");
384 	use_ssl = (tmp && ((*tmp == 't') || (*tmp == 'T'))) ? TRUE : FALSE;
385 	tmp = gda_quark_list_find (params, "USE_CACHE");
386 	use_cache = (!tmp || ((*tmp == 't') || (*tmp == 'T'))) ? TRUE : FALSE;
387 	if (port && *port)
388 		rport = atoi (port);
389 	else {
390 		if (use_ssl)
391 			rport = LDAPS_PORT;
392 		else
393 			rport = LDAP_PORT;
394 	}
395 	user = gda_quark_list_find (auth, "USERNAME");
396         if (!user)
397                 user = gda_quark_list_find (params, "USERNAME");
398         pwd = gda_quark_list_find (auth, "PASSWORD");
399         if (!pwd)
400                 pwd = gda_quark_list_find (params, "PASSWORD");
401 
402 	tls_cacert = gda_quark_list_find (params, "TLS_CACERT");
403 	tls_method = gda_quark_list_find (params, "TLS_REQCERT");
404 	if (tls_method && *tls_method) {
405 		if (! g_ascii_strcasecmp (tls_method, "never"))
406 			rtls_method = LDAP_OPT_X_TLS_NEVER;
407 		else if (! g_ascii_strcasecmp (tls_method, "hard"))
408 			rtls_method = LDAP_OPT_X_TLS_HARD;
409 		else if (! g_ascii_strcasecmp (tls_method, "demand"))
410 			rtls_method = LDAP_OPT_X_TLS_DEMAND;
411 		else if (! g_ascii_strcasecmp (tls_method, "allow"))
412 			rtls_method = LDAP_OPT_X_TLS_ALLOW;
413 		else if (! g_ascii_strcasecmp (tls_method, "try"))
414 			rtls_method = LDAP_OPT_X_TLS_TRY;
415 		else {
416 			gda_connection_add_event_string (cnc, "%s",
417 							 _("Invalid value for 'TLS_REQCERT'"));
418 			return FALSE;
419 		}
420 	}
421 	time_limit = gda_quark_list_find (params, "TIME_LIMIT");
422 	size_limit = gda_quark_list_find (params, "SIZE_LIMIT");
423 
424 	/* open LDAP connection */
425 	LdapConnectionData *cdata;
426 	LDAP *ld;
427         int res;
428 	gchar *url;
429 
430 	if (use_ssl) {
431 		/* Configuring SSL/TLS options:
432 		 * this is for texting purpose only, and should actually be done through LDAP's conf.
433 		 * files, see: man 5 ldap.conf
434 		 *
435 		 * For example ~/.ldaprc can contain:
436 		 * TLS_REQCERT demand
437 		 * TLS_CACERT /usr/share/ca-certificates/mozilla/Thawte_Premium_Server_CA.crt
438 		 *
439 		 * Note: if server certificate verification fails,
440 		 * the error message is: "Can't contact LDAP server"
441 		 */
442 		if (rtls_method >= 0) {
443 			res = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &rtls_method);
444 			if (res != LDAP_SUCCESS) {
445 				gda_connection_add_event_string (cnc, ldap_err2string (res));
446 				return FALSE;
447 			}
448 		}
449 
450 		if (tls_cacert && *tls_cacert) {
451 			res = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, tls_cacert);
452 			if (res != LDAP_SUCCESS) {
453 				gda_connection_add_event_string (cnc, ldap_err2string (res));
454 				return FALSE;
455 			}
456 		}
457 
458 		url = g_strdup_printf ("ldaps://%s:%d", host, rport);
459 	}
460 	else
461 		url = g_strdup_printf ("ldap://%s:%d", host, rport);
462 
463 	if (user && *user && ! gda_ldap_parse_dn (user, NULL)) {
464 		/* analysing the @user parameter */
465 		/* the user name is not a DN => we need to fetch the DN of the entry
466 		 * using filters defined in the "mappings" array @user */
467 		guint i;
468 		const gchar *ptr;
469 		GString *rname;
470 		rname = g_string_new ("");
471 		for (ptr = user; *ptr; ptr++) {
472 			if ((*ptr == ',') || (*ptr == '\\') || (*ptr == '#') ||
473 			    (*ptr == '+') || (*ptr == '<') ||
474 			    (*ptr == '>') || (*ptr == ';') || (*ptr == '"') ||
475 			    (*ptr == '=') || (*ptr == '*'))
476 				g_string_append_c (rname, '\\');
477 			g_string_append_c (rname, *ptr);
478 		}
479 		for (i = 0; i < sizeof (mappings) / sizeof (LdapAuthMapping); i++) {
480 			gchar *tmp;
481 			tmp = fetch_user_dn (url, base_dn, rname->str, &(mappings[i]));
482 			if (tmp) {
483 				dnuser = tmp;
484 				break;
485 			}
486 		}
487 		g_string_free (rname, TRUE);
488 
489 		/* if no DN user has been found, then still use the provided name AS IS
490 		 * => dnuser can be %NULL here */
491 	}
492 
493 	res = ldap_initialize (&ld, url);
494         if (res != LDAP_SUCCESS) {
495 		gda_connection_add_event_string (cnc, ldap_err2string (res));
496 		g_free (url);
497 		g_free (dnuser);
498                 return FALSE;
499         }
500 
501 	cdata = g_new0 (LdapConnectionData, 1);
502 	cdata->keep_bound_count = 0;
503 	cdata->handle = ld;
504 	cdata->url = url;
505 	cdata->time_limit = 0;
506 	cdata->size_limit = 0;
507 	cdata->base_dn = g_strdup (base_dn);
508 	if (use_cache)
509 		cdata->attributes_cache_file = compute_data_file_name (params, TRUE, "attrs");
510 
511 	/* set protocol version to 3 by default */
512 	int version = LDAP_VERSION3;
513 	res = ldap_set_option (cdata->handle, LDAP_OPT_PROTOCOL_VERSION, &version);
514         if (res != LDAP_SUCCESS) {
515 		if (res == LDAP_PROTOCOL_ERROR) {
516 			version = LDAP_VERSION2;
517 			res = ldap_set_option (cdata->handle, LDAP_OPT_PROTOCOL_VERSION, &version);
518 		}
519 		if (res != LDAP_SUCCESS) {
520 			gda_connection_add_event_string (cnc, ldap_err2string (res));
521 			gda_ldap_free_cnc_data (cdata);
522 			g_free (dnuser);
523 			return FALSE;
524 		}
525         }
526 
527 	/* time limit */
528 	if (time_limit && *time_limit) {
529 		int limit = atoi (time_limit);
530 		res = ldap_set_option (cdata->handle, LDAP_OPT_TIMELIMIT, &limit);
531 		if (res != LDAP_SUCCESS) {
532 			gda_connection_add_event_string (cnc, ldap_err2string (res));
533 			gda_ldap_free_cnc_data (cdata);
534 			g_free (dnuser);
535 			return FALSE;
536 		}
537 		cdata->time_limit = limit;
538 	}
539 
540 	/* size limit */
541 	if (size_limit && *size_limit) {
542 		int limit = atoi (size_limit);
543 		res = ldap_set_option (cdata->handle, LDAP_OPT_SIZELIMIT, &limit);
544 		if (res != LDAP_SUCCESS) {
545 			gda_connection_add_event_string (cnc, ldap_err2string (res));
546 			gda_ldap_free_cnc_data (cdata);
547 			g_free (dnuser);
548 			return FALSE;
549 		}
550 		cdata->size_limit = limit;
551 	}
552 
553 	/* authentication */
554 	struct berval cred;
555         memset (&cred, 0, sizeof (cred));
556         cred.bv_len = pwd && *pwd ? strlen (pwd) : 0;
557         cred.bv_val = pwd && *pwd ? (char *) pwd : NULL;
558         res = ldap_sasl_bind_s (ld, dnuser ? dnuser : user, NULL, &cred, NULL, NULL, NULL);
559 	if (res != LDAP_SUCCESS) {
560 		gda_connection_add_event_string (cnc, ldap_err2string (res));
561 		gda_ldap_free_cnc_data (cdata);
562 		g_free (dnuser);
563                 return FALSE;
564 	}
565 	if (pwd) {
566 		gchar *tmp;
567 		tmp = g_strdup_printf ("PASSWORD=%s", pwd);
568 		cdata->auth = gda_quark_list_new_from_string (tmp);
569 		g_free (tmp);
570 	}
571 	if (dnuser) {
572 		gchar *tmp;
573 		tmp = g_strdup_printf ("USERNAME=%s", dnuser);
574 		if (cdata->auth)
575 			gda_quark_list_add_from_string (cdata->auth, tmp, FALSE);
576 		else
577 			cdata->auth = gda_quark_list_new_from_string (tmp);
578 		g_free (tmp);
579 		dnuser = NULL;
580 	}
581 	else if (user) {
582 		gchar *tmp;
583 		tmp = g_strdup_printf ("USERNAME=%s", user);
584 		if (cdata->auth)
585 			gda_quark_list_add_from_string (cdata->auth, tmp, FALSE);
586 		else
587 			cdata->auth = gda_quark_list_new_from_string (tmp);
588 		g_free (tmp);
589 	}
590 
591 	/* set startup file name */
592 	gchar *fname;
593 	fname = compute_data_file_name (params, FALSE, "start");
594 	g_object_set ((GObject*) cnc, "startup-file", fname, NULL);
595 	g_free (fname);
596 
597 	/* open virtual connection */
598 	g_object_set_data ((GObject*) cnc, "__gda_connection_LDAP", (gpointer) 0x01);
599 	gda_virtual_connection_internal_set_provider_data (GDA_VIRTUAL_CONNECTION (cnc),
600 							   cdata, (GDestroyNotify) gda_ldap_free_cnc_data);
601 	if (! GDA_SERVER_PROVIDER_CLASS (parent_class)->open_connection (GDA_SERVER_PROVIDER (provider), cnc, params,
602                                                                          NULL, NULL, NULL, NULL)) {
603 		gda_virtual_connection_internal_set_provider_data (GDA_VIRTUAL_CONNECTION (cnc), NULL, NULL);
604                 gda_connection_add_event_string (cnc, _("Can't open virtual connection"));
605 		gda_ldap_free_cnc_data (cdata);
606                 return FALSE;
607         }
608 
609 	gda_ldap_may_unbind (cdata);
610 	return TRUE;
611 }
612 
613 /*
614  * Unbinds the connection if possible (i.e. if cdata->keep_bound_count is 0)
615  * This allows to avoid keeping the connection to the LDAP server if unused
616  */
617 void
gda_ldap_may_unbind(LdapConnectionData * cdata)618 gda_ldap_may_unbind (LdapConnectionData *cdata)
619 {
620 	if (!cdata || (cdata->keep_bound_count > 0))
621 		return;
622 	if (cdata->handle) {
623                 ldap_unbind_ext (cdata->handle, NULL, NULL);
624 		cdata->handle = NULL;
625 	}
626 }
627 
628 /*
629  * Makes sure the connection is opened
630  */
631 gboolean
gda_ldap_ensure_bound(LdapConnectionData * cdata,GError ** error)632 gda_ldap_ensure_bound (LdapConnectionData *cdata, GError **error)
633 {
634 	if (!cdata)
635 		return FALSE;
636 	else if (cdata->handle)
637 		return TRUE;
638 
639 	return gda_ldap_rebind (cdata, error);
640 }
641 
642 /*
643  * Reopens a connection after the server has closed it (possibly because of a timeout)
644  *
645  * If it fails, then @cdata is left unchanged, otherwise it is modified to be useable again.
646  */
647 gboolean
gda_ldap_rebind(LdapConnectionData * cdata,GError ** error)648 gda_ldap_rebind (LdapConnectionData *cdata, GError **error)
649 {
650 	if (!cdata)
651 		return FALSE;
652 
653 	/*g_print ("Trying to reconnect...\n");*/
654 	LDAP *ld;
655 	int res;
656 	res = ldap_initialize (&ld, cdata->url);
657 	if (res != LDAP_SUCCESS) {
658 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
659 			     "%s", ldap_err2string (res));
660 		return FALSE;
661 	}
662 
663 	/* set protocol version to 3 by default */
664 	int version = LDAP_VERSION3;
665 	res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
666         if (res != LDAP_SUCCESS) {
667 		if (res == LDAP_PROTOCOL_ERROR) {
668 			version = LDAP_VERSION2;
669 			res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
670 		}
671 		if (res != LDAP_SUCCESS) {
672 			g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
673 				     "%s", ldap_err2string (res));
674 			ldap_unbind_ext (ld, NULL, NULL);
675 			return FALSE;
676 		}
677         }
678 
679 	/* authentication */
680 	struct berval cred;
681 	const gchar *pwd = "";
682 	const gchar *user = "";
683 	if (cdata->auth)
684 		pwd = gda_quark_list_find (cdata->auth, "PASSWORD");
685         memset (&cred, 0, sizeof (cred));
686         cred.bv_len = pwd && *pwd ? strlen (pwd) : 0;
687         cred.bv_val = pwd && *pwd ? (char *) pwd : NULL;
688 
689 	if (cdata->auth)
690 		user = gda_quark_list_find (cdata->auth, "USERNAME");
691 	res = ldap_sasl_bind_s (ld, user, NULL, &cred, NULL, NULL, NULL);
692 	if (cdata->auth)
693 		gda_quark_list_protect_values (cdata->auth);
694 
695 	if (res != LDAP_SUCCESS) {
696 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
697 			     "%s", ldap_err2string (res));
698 		ldap_unbind_ext (ld, NULL, NULL);
699                 return FALSE;
700 	}
701 
702 	/* time limit */
703 	int limit = cdata->time_limit;
704 	res = ldap_set_option (cdata->handle, LDAP_OPT_TIMELIMIT, &limit);
705 	if (res != LDAP_SUCCESS) {
706 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
707 			     "%s", ldap_err2string (res));
708 		ldap_unbind_ext (ld, NULL, NULL);
709                 return FALSE;
710 	}
711 
712 	/* size limit */
713 	limit = cdata->size_limit;
714 	res = ldap_set_option (cdata->handle, LDAP_OPT_SIZELIMIT, &limit);
715 	if (res != LDAP_SUCCESS) {
716 		g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_OPEN_ERROR,
717 			     "%s", ldap_err2string (res));
718 		ldap_unbind_ext (ld, NULL, NULL);
719                 return FALSE;
720 	}
721 
722 	/* all ok */
723 	if (cdata->handle) {
724 		/* don't call ldap_unbind_ext() as it often crashed the application */
725 		/*ldap_unbind_ext (cdata->handle, NULL, NULL);*/
726 	}
727 	cdata->handle = ld;
728 
729 	/*g_print ("Reconnected!\n");*/
730 	return TRUE;
731 }
732 
733 /*
734  * Server version request
735  */
736 static const gchar *
gda_ldap_provider_get_server_version(GdaServerProvider * provider,GdaConnection * cnc)737 gda_ldap_provider_get_server_version (GdaServerProvider *provider, GdaConnection *cnc)
738 {
739 	LdapConnectionData *cdata;
740 
741         g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
742         g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
743 
744         cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
745         if (!cdata)
746                 return FALSE;
747 
748 	if (! cdata->server_version) {
749 		/* FIXME: don't know how to get information about the LDAP server! */
750 	}
751         return cdata->server_version;
752 }
753 
754 /*
755  * Get database request
756  */
757 static const gchar *
gda_ldap_provider_get_database(GdaServerProvider * provider,GdaConnection * cnc)758 gda_ldap_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
759 {
760 	LdapConnectionData *cdata;
761 
762         g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
763         g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, NULL);
764 
765         cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (cnc));
766         if (!cdata)
767                 return NULL;
768         return cdata->base_dn;
769 }
770 
771 /*
772  * Extra SQL
773  */
774 typedef struct {
775 	gchar             *table_name;
776 	gboolean           other_args; /* set to %TRUE if any of the arguments below have been specified */
777 	gchar             *base_dn;
778 	gchar             *filter;
779 	gchar             *attributes;
780 	GdaLdapSearchScope scope;
781 } ExtraSqlCommand;
782 static void extra_sql_command_free (ExtraSqlCommand *cmde);
783 
784 #define NOT_AN_EXTRA_SQL_COMMAND (ExtraSqlCommand*) 0x01
785 #define SKIP_SPACES(x) for (; *(x) && (g_ascii_isspace (*(x)) || (*(x)=='\n')); (x)++)
786 static ExtraSqlCommand *parse_extra_sql_command (gchar *cmd, const gchar *cmde_name,
787 						 GError **error);
788 static GdaDataModel *table_parameters_describe (const gchar *base_dn, const gchar *filter,
789 						const gchar *attributes,
790 						GdaLdapSearchScope scope);
791 static GObject *
gda_ldap_provider_statement_execute(GdaServerProvider * provider,GdaConnection * cnc,GdaStatement * stmt,GdaSet * params,GdaStatementModelUsage model_usage,GType * col_types,GdaSet ** last_inserted_row,guint * task_id,GdaServerProviderExecCallback async_cb,gpointer cb_data,GError ** error)792 gda_ldap_provider_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
793 				     GdaStatement *stmt, GdaSet *params,
794 				     GdaStatementModelUsage model_usage,
795 				     GType *col_types, GdaSet **last_inserted_row,
796 				     guint *task_id, GdaServerProviderExecCallback async_cb,
797 				     gpointer cb_data, GError **error)
798 {
799 	if (async_cb) {
800                 g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
801 			     "%s", _("Provider does not support asynchronous statement execution"));
802                 return NULL;
803         }
804 	gchar *sql;
805 	sql = gda_statement_to_sql (stmt, params, NULL);
806 	if (sql) {
807 		/* parse SQL:
808 		 * CREATE LDAP TABLE <table name> WITH BASE='base_dn' FILTER='filter'
809 		 *              ATTRIBUTES='attributes' SCOPE='scope'
810 		 */
811 		gchar *ssql = sql;
812 		SKIP_SPACES (ssql);
813 		if (! g_ascii_strncasecmp (ssql, "CREATE", 6)) {
814 			ExtraSqlCommand *cmde;
815 			GError *lerror = NULL;
816 			GObject *retval = NULL;
817 			cmde = parse_extra_sql_command (ssql, "CREATE", &lerror);
818 			if (cmde != NOT_AN_EXTRA_SQL_COMMAND) {
819 				GdaConnectionEvent *event = NULL;
820 				if (cmde) {
821 					if (gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (cnc),
822 									       cmde->table_name, cmde->base_dn,
823 									       cmde->filter, cmde->attributes,
824 									       cmde->scope, &lerror))
825 					retval = (GObject*) gda_set_new (NULL);
826 					else {
827 						event = gda_connection_point_available_event (cnc,
828 											      GDA_CONNECTION_EVENT_ERROR);
829 						gda_connection_event_set_description (event, lerror && lerror->message ?
830 										      lerror->message : _("No detail"));
831 						gda_connection_add_event (cnc, event);
832 						g_propagate_error (error, lerror);
833 					}
834 					extra_sql_command_free (cmde);
835 				}
836 				else {
837 					event = gda_connection_point_available_event (cnc,
838 										      GDA_CONNECTION_EVENT_ERROR);
839 					gda_connection_event_set_description (event, lerror && lerror->message ?
840 									      lerror->message : _("No detail"));
841 					gda_connection_add_event (cnc, event);
842 					g_propagate_error (error, lerror);
843 				}
844 
845 				gda_connection_internal_statement_executed (cnc, stmt, params, event);
846 				g_free (sql);
847 				return retval;
848 			}
849 		}
850 
851 		/* parse SQL:
852 		 * DROP LDAP TABLE <table name>
853 		 */
854 		else if (! g_ascii_strncasecmp (ssql, "DROP", 4)) {
855 			ExtraSqlCommand *cmde;
856 			GError *lerror = NULL;
857 			GObject *retval = NULL;
858 			cmde = parse_extra_sql_command (ssql, "DROP", &lerror);
859 			if (cmde != NOT_AN_EXTRA_SQL_COMMAND) {
860 				GdaConnectionEvent *event = NULL;
861 				if (cmde) {
862 					if (cmde->other_args) {
863 						g_set_error (&lerror, GDA_SQL_PARSER_ERROR,
864 							     GDA_SQL_PARSER_SYNTAX_ERROR,
865 							     "%s",
866 							     _("Too many arguments"));
867 						event = gda_connection_point_available_event (cnc,
868 											      GDA_CONNECTION_EVENT_ERROR);
869 						gda_connection_event_set_description (event,
870 										      lerror->message);
871 						gda_connection_add_event (cnc, event);
872 						g_propagate_error (error, lerror);
873 					}
874 					else {
875 						if (gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (cnc),
876 											 cmde->table_name, &lerror))
877 							retval = (GObject*) gda_set_new (NULL);
878 						else {
879 							event = gda_connection_point_available_event (cnc,
880 												      GDA_CONNECTION_EVENT_ERROR);
881 							gda_connection_event_set_description (event, lerror && lerror->message ?
882 											      lerror->message : _("No detail"));
883 							gda_connection_add_event (cnc, event);
884 							g_propagate_error (error, lerror);
885 						}
886 					}
887 					extra_sql_command_free (cmde);
888 				}
889 				else {
890 					event = gda_connection_point_available_event (cnc,
891 										      GDA_CONNECTION_EVENT_ERROR);
892 					gda_connection_event_set_description (event, lerror && lerror->message ?
893 									      lerror->message : _("No detail"));
894 					gda_connection_add_event (cnc, event);
895 					g_propagate_error (error, lerror);
896 				}
897 				gda_connection_internal_statement_executed (cnc, stmt, params, event);
898 				g_free (sql);
899 				return retval;
900 			}
901 		}
902 		/* parse SQL:
903 		 * ALTER LDAP TABLE <table name> [...]
904 		 * DESCRIBE LDAP TABLE <table name>
905 		 */
906 		else if (! g_ascii_strncasecmp (ssql, "ALTER", 5) ||
907 			 ! g_ascii_strncasecmp (ssql, "DESCRIBE", 8)) {
908 			ExtraSqlCommand *cmde;
909 			GError *lerror = NULL;
910 			GObject *retval = NULL;
911 			gboolean alter;
912 
913 			alter = g_ascii_strncasecmp (ssql, "ALTER", 5) ? FALSE : TRUE;
914 
915 			cmde = parse_extra_sql_command (ssql, alter ? "ALTER" : "DESCRIBE", &lerror);
916 			if ((cmde != NOT_AN_EXTRA_SQL_COMMAND) &&
917 			    (alter || (!alter && !cmde->other_args))) {
918 				GdaConnectionEvent *event = NULL;
919 				if (cmde) {
920 					const gchar *base_dn, *filter, *attributes;
921 					GdaLdapSearchScope scope;
922 					if (gda_ldap_connection_describe_table (GDA_LDAP_CONNECTION (cnc),
923 										cmde->table_name,
924 										&base_dn, &filter,
925 										&attributes, &scope, &lerror)) {
926 						if (cmde->other_args) {
927 							if (! cmde->base_dn && base_dn)
928 								cmde->base_dn = g_strdup (base_dn);
929 							if (! cmde->filter && filter)
930 								cmde->filter = g_strdup (filter);
931 							if (! cmde->attributes && attributes)
932 								cmde->attributes = g_strdup (attributes);
933 							if (! cmde->scope)
934 								cmde->scope = scope;
935 							if (gda_ldap_connection_undeclare_table (GDA_LDAP_CONNECTION (cnc),
936 												 cmde->table_name, &lerror) &&
937 							    gda_ldap_connection_declare_table (GDA_LDAP_CONNECTION (cnc),
938 											       cmde->table_name, cmde->base_dn,
939 											       cmde->filter, cmde->attributes,
940 											       cmde->scope, &lerror))
941 								retval = (GObject*) gda_set_new (NULL);
942 						}
943 						else {
944 							GdaDataModel *array;
945 							array = table_parameters_describe (base_dn, filter,
946 											   attributes, scope);
947 							retval = (GObject*) array;
948 						}
949 					}
950 					if (!retval) {
951 						event = gda_connection_point_available_event (cnc,
952 											      GDA_CONNECTION_EVENT_ERROR);
953 						gda_connection_event_set_description (event, lerror && lerror->message ?
954 										      lerror->message : _("No detail"));
955 						gda_connection_add_event (cnc, event);
956 						g_propagate_error (error, lerror);
957 					}
958 					extra_sql_command_free (cmde);
959 				}
960 				gda_connection_internal_statement_executed (cnc, stmt, params, event);
961 				g_free (sql);
962 				return retval;
963 			}
964 		}
965 		g_free (sql);
966 	}
967 	return GDA_SERVER_PROVIDER_CLASS (parent_class)->statement_execute (provider, cnc, stmt, params,
968 									    model_usage, col_types,
969 									    last_inserted_row, task_id,
970 									    async_cb, cb_data, error);
971 }
972 
973 /*
974  * Free connection's specific data
975  */
976 static void
gda_ldap_free_cnc_data(LdapConnectionData * cdata)977 gda_ldap_free_cnc_data (LdapConnectionData *cdata)
978 {
979 	if (cdata->handle)
980                 ldap_unbind_ext (cdata->handle, NULL, NULL);
981 	if (cdata->attributes_hash)
982 		g_hash_table_destroy (cdata->attributes_hash);
983 	g_free (cdata->attributes_cache_file);
984 	g_free (cdata->base_dn);
985 	g_free (cdata->server_version);
986 	g_free (cdata->url);
987 	if (cdata->auth)
988 		gda_quark_list_free (cdata->auth);
989 	g_free (cdata);
990 }
991 
992 static const gchar *
scope_to_string(GdaLdapSearchScope scope)993 scope_to_string (GdaLdapSearchScope scope)
994 {
995 	switch (scope) {
996 	case GDA_LDAP_SEARCH_BASE:
997 		return "BASE";
998 	case GDA_LDAP_SEARCH_ONELEVEL:
999 		return "ONELEVEL";
1000 	case GDA_LDAP_SEARCH_SUBTREE:
1001 		return "SUBTREE";
1002 	default:
1003 		return _("Unknown");
1004 	}
1005 }
1006 
1007 static GdaDataModel *
table_parameters_describe(const gchar * base_dn,const gchar * filter,const gchar * attributes,GdaLdapSearchScope scope)1008 table_parameters_describe (const gchar *base_dn, const gchar *filter, const gchar *attributes,
1009 			   GdaLdapSearchScope scope)
1010 {
1011 	GdaDataModel *array;
1012 	GValue *v1, *v2;
1013 	GList *list;
1014 	array = gda_data_model_array_new_with_g_types (2, G_TYPE_STRING,
1015 						       G_TYPE_STRING);
1016 	gda_data_model_set_column_title (array, 0, _("Parameter"));
1017 	gda_data_model_set_column_title (array, 1, _("Value"));
1018 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "BASE");
1019 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), base_dn);
1020 	list = g_list_append (NULL, v1);
1021 	list = g_list_append (list, v2);
1022 	gda_data_model_append_values (array, list, NULL);
1023 	g_list_free (list);
1024 	gda_value_free (v1);
1025 	gda_value_free (v2);
1026 
1027 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "FILTER");
1028 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), filter);
1029 	list = g_list_append (NULL, v1);
1030 	list = g_list_append (list, v2);
1031 	gda_data_model_append_values (array, list, NULL);
1032 	g_list_free (list);
1033 	gda_value_free (v1);
1034 	gda_value_free (v2);
1035 
1036 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "ATTRIBUTES");
1037 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), attributes);
1038 	list = g_list_append (NULL, v1);
1039 	list = g_list_append (list, v2);
1040 	gda_data_model_append_values (array, list, NULL);
1041 	g_list_free (list);
1042 	gda_value_free (v1);
1043 	gda_value_free (v2);
1044 
1045 	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "SCOPE");
1046 	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), scope_to_string (scope));
1047 	list = g_list_append (NULL, v1);
1048 	list = g_list_append (list, v2);
1049 	gda_data_model_append_values (array, list, NULL);
1050 	g_list_free (list);
1051 	gda_value_free (v1);
1052 	gda_value_free (v2);
1053 	return array;
1054 }
1055 
1056 /*
1057  * Extra commands parsing
1058  */
1059 /*
1060  * consumes @str for a singly quoted string
1061  * Returns: a pointer to the next non analysed char
1062  */
1063 static gchar *
parse_string(gchar * str,gchar ** out_part)1064 parse_string (gchar *str, gchar **out_part)
1065 {
1066 	gchar *ptr = str;
1067 	*out_part = NULL;
1068 
1069 	SKIP_SPACES (ptr);
1070 	if (!*ptr)
1071 		return NULL;
1072 	if (*ptr != '\'') {
1073 		if (!g_ascii_strncasecmp (ptr, "null", 4)) {
1074 			ptr += 4;
1075 			return ptr;
1076 		}
1077 		else
1078 			return NULL;
1079 	}
1080 	ptr++;
1081 	*out_part = ptr;
1082 	for (; *ptr && (*ptr != '\''); ptr++);
1083 	if (!*ptr)
1084 		return NULL;
1085 	*ptr = 0;
1086 	ptr++;
1087 	return ptr;
1088 }
1089 
1090 /*
1091  * consumes @str for an identifier
1092  * Returns: a pointer to the next non analysed char
1093  */
1094 static gchar *
parse_ident(gchar * str,gchar ** out_part)1095 parse_ident (gchar *str, gchar **out_part)
1096 {
1097 	gchar *ptr = str;
1098 	*out_part = NULL;
1099 
1100 	SKIP_SPACES (ptr);
1101 	*out_part = ptr;
1102 	for (; *ptr && (g_ascii_isalnum (*ptr) || (*ptr == '_')) ; ptr++);
1103 	if (ptr == *out_part) {
1104 		*out_part = NULL;
1105 		return NULL;
1106 	}
1107 	return ptr;
1108 }
1109 
1110 static void
extra_sql_command_free(ExtraSqlCommand * cmde)1111 extra_sql_command_free (ExtraSqlCommand *cmde)
1112 {
1113 	g_free (cmde->table_name);
1114 	g_free (cmde->base_dn);
1115 	g_free (cmde->filter);
1116 	g_free (cmde->attributes);
1117 	g_free (cmde);
1118 }
1119 
1120 /*
1121  * CREATE LDAP TABLE <table name> BASE='base_dn' FILTER='filter' ATTRIBUTES='attributes' SCOPE='scope'
1122  * DROP LDAP TABLE <table name>
1123  * ALTER LDAP TABLE <table name>
1124  * ALTER LDAP TABLE <table name> BASE='base_dn' FILTER='filter' ATTRIBUTES='attributes' SCOPE='scope'
1125  *
1126  * Returns: a #ExtraSqlCommand pointer, or %NULL, or NOT_AN_EXTRA_SQL_COMMAND (if not "CREATE LDAP...")
1127  */
1128 static ExtraSqlCommand *
parse_extra_sql_command(gchar * cmd,const gchar * cmde_name,GError ** error)1129 parse_extra_sql_command (gchar *cmd, const gchar *cmde_name, GError **error)
1130 {
1131 	ExtraSqlCommand *args;
1132 	gchar *ptr, *errptr, *part, *tmp;
1133 	args = g_new0 (ExtraSqlCommand, 1);
1134 	args->other_args = FALSE;
1135 
1136 	ptr = cmd + strlen (cmde_name);
1137 
1138 	/* make sure about complete command */
1139 	errptr = ptr;
1140 	if (! (ptr = parse_ident (ptr, &part)))
1141 		return NOT_AN_EXTRA_SQL_COMMAND;
1142 	if (!part || g_ascii_strncasecmp (part, "ldap", 4))
1143 		return NOT_AN_EXTRA_SQL_COMMAND;
1144 
1145 	errptr = ptr;
1146 	if (! (ptr = parse_ident (ptr, &part)))
1147 		goto onerror;
1148 	if (!part || g_ascii_strncasecmp (part, "table", 5))
1149 		goto onerror;
1150 
1151 	/* table name */
1152 	SKIP_SPACES (ptr);
1153 	errptr = ptr;
1154 	if (! (ptr = parse_ident (ptr, &part)))
1155 		goto onerror;
1156 	tmp = g_strndup (part, ptr-part);
1157 	args->table_name = g_ascii_strdown (tmp, -1);
1158 	g_free (tmp);
1159 
1160 	/* key=value arguments */
1161 	while (TRUE) {
1162 		errptr = ptr;
1163 		SKIP_SPACES (ptr);
1164 		if (! (ptr = parse_ident (ptr, &part))) {
1165 			ptr = errptr;
1166 			break;
1167 		}
1168 		if (part) {
1169 			gchar **where = NULL;
1170 			if (!g_ascii_strncasecmp (part, "base", 4))
1171 				where = &(args->base_dn);
1172 			else if (!g_ascii_strncasecmp (part, "filter", 6))
1173 				where = &(args->filter);
1174 			else if (!g_ascii_strncasecmp (part, "attributes", 10))
1175 				where = &(args->attributes);
1176 			else if (!g_ascii_strncasecmp (part, "scope", 5))
1177 				where = NULL;
1178 			else
1179 				goto onerror;
1180 
1181 			/* = */
1182 			errptr = ptr;
1183 			SKIP_SPACES (ptr);
1184 			if (*ptr != '=')
1185 				goto onerror;
1186 			ptr++;
1187 
1188 			/* value */
1189 			errptr = ptr;
1190 			SKIP_SPACES (ptr);
1191 			if (! (ptr = parse_string (ptr, &part)))
1192 				goto onerror;
1193 			if (part) {
1194 				if (where)
1195 					*where = g_strdup (part);
1196 				else {
1197 					if (!g_ascii_strcasecmp (part, "base"))
1198 						args->scope = GDA_LDAP_SEARCH_BASE;
1199 					else if (!g_ascii_strcasecmp (part, "onelevel"))
1200 						args->scope = GDA_LDAP_SEARCH_ONELEVEL;
1201 					else if (!g_ascii_strcasecmp (part, "subtree"))
1202 						args->scope = GDA_LDAP_SEARCH_SUBTREE;
1203 					else
1204 						goto onerror;
1205 				}
1206 				args->other_args = TRUE;
1207 			}
1208 			else
1209 				goto onerror;
1210 		}
1211 		else
1212 			break;
1213 	}
1214 
1215 	/* end */
1216 	SKIP_SPACES (ptr);
1217 	if (*ptr && (*ptr != ';'))
1218 		goto onerror;
1219 #ifdef GDA_DEBUG_NO
1220 	g_print ("TABLE=>%s, BASE=>%s, FILTER=>%s, ATTRIBUTES=>%s, SCOPE=>%d\n", args->table_name,
1221 		 args->base_dn, args->filter,
1222 		 args->attributes, args->scope);
1223 #endif
1224 
1225 	return args;
1226 
1227  onerror:
1228 	SKIP_SPACES (errptr);
1229 	g_set_error (error, GDA_SQL_PARSER_ERROR, GDA_SQL_PARSER_SYNTAX_ERROR,
1230 		     _("near \"%s\": syntax error"), errptr);
1231 	extra_sql_command_free (args);
1232 	return NULL;
1233 }
1234