1 /*
2  * Copyright (C) 2011 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19 
20 #include <glib/gi18n-lib.h>
21 #include <string.h>
22 #include <gda-util.h>
23 #include "gda-ldap-connection.h"
24 #include <libgda/gda-connection-private.h>
25 #include <sql-parser/gda-sql-parser.h>
26 #include <libgda/gda-debug-macros.h>
27 
28 /* "inherits" GdaVconnectionDataModelSpec */
29 typedef struct {
30 	GdaVconnectionDataModelSpec  spec;
31 	GdaConnection               *ldap_cnc;
32 	gchar                       *table_name;
33 	gchar                       *base_dn;
34 	gchar                       *filter;
35 	gchar                       *attributes;
36 	GList                       *columns;
37 	GdaLdapSearchScope           scope;
38 
39 	GHashTable                  *filters_hash; /* key = string; value = a ComputedFilter pointer */
40 } LdapTableMap;
41 
42 static void
_ldap_table_map_free(LdapTableMap * map)43 _ldap_table_map_free (LdapTableMap *map)
44 {
45 	if (map->ldap_cnc)
46 		g_object_unref (map->ldap_cnc);
47 	g_free (map->table_name);
48 	g_free (map->base_dn);
49 	g_free (map->filter);
50 	g_free (map->attributes);
51 	if (map->columns) {
52 		g_list_foreach (map->columns, (GFunc) g_object_unref, NULL);
53 		g_list_free (map->columns);
54 	}
55 	if (map->filters_hash)
56 		g_hash_table_destroy (map->filters_hash);
57 	g_free (map);
58 }
59 
60 struct _GdaLdapConnectionPrivate {
61 	GSList   *maps; /* list of #LdapTableMap, no ref held there */
62 	gchar    *startup_file;
63 	gboolean  loading_startup_file;
64 };
65 
66 static void gda_ldap_connection_class_init (GdaLdapConnectionClass *klass);
67 static void gda_ldap_connection_init       (GdaLdapConnection *cnc, GdaLdapConnectionClass *klass);
68 static void gda_ldap_connection_dispose    (GObject *object);
69 static void gda_ldap_connection_set_property (GObject *object,
70 					      guint param_id,
71 					      const GValue *value,
72 					      GParamSpec *pspec);
73 static void gda_ldap_connection_get_property (GObject *object,
74 					      guint param_id,
75 					      GValue *value,
76 					      GParamSpec *pspec);
77 
78 static GObjectClass *parent_class = NULL;
79 
80 /* properties */
81 enum
82 {
83         PROP_0,
84 	PROP_STARTUP_FILE
85 };
86 
87 static void
update_connection_startup_file(GdaLdapConnection * cnc)88 update_connection_startup_file (GdaLdapConnection *cnc)
89 {
90 	if (! cnc->priv->startup_file || cnc->priv->loading_startup_file)
91 		return;
92 
93 	GSList *list;
94 	GString *string = NULL;
95 	GError *lerror = NULL;
96 
97 	string = g_string_new ("");
98 	for (list = cnc->priv->maps; list; list = list->next) {
99 		LdapTableMap *map = (LdapTableMap*) list->data;
100 		g_string_append_printf (string, "CREATE LDAP TABLE %s ", map->table_name);
101 		if (map->base_dn)
102 			g_string_append_printf (string, "BASE='%s' ", map->base_dn);
103 		if (map->filter)
104 			g_string_append_printf (string, "FILTER='%s' ", map->filter);
105 		if (map->attributes)
106 			g_string_append_printf (string, "ATTRIBUTES='%s' ", map->attributes);
107 		g_string_append (string, "SCOPE=");
108 		switch (map->scope) {
109 		case GDA_LDAP_SEARCH_BASE:
110 			g_string_append (string, "'BASE';\n");
111 			break;
112 		case GDA_LDAP_SEARCH_ONELEVEL:
113 			g_string_append (string, "'ONELEVEL';\n");
114 			break;
115 		case GDA_LDAP_SEARCH_SUBTREE:
116 			g_string_append (string, "'SUBTREE';\n");
117 			break;
118 		default:
119 			g_assert_not_reached ();
120 		}
121 	}
122 	if (! g_file_set_contents (cnc->priv->startup_file, string->str, -1, &lerror)) {
123 		GdaConnectionEvent *event;
124 		gchar *msg;
125 		event = gda_connection_point_available_event (GDA_CONNECTION (cnc),
126 							      GDA_CONNECTION_EVENT_WARNING);
127 		msg = g_strdup_printf (_("Error storing list of created LDAP tables: %s"),
128 				       lerror && lerror->message ? lerror->message : _("No detail"));
129 		gda_connection_event_set_description (event, msg);
130 		gda_connection_add_event (GDA_CONNECTION (cnc), event);
131 		g_free (msg);
132 		g_clear_error (&lerror);
133 	}
134 }
135 
136 #ifdef GDA_DEBUG_NO
137 static void
dump_vtables(GdaLdapConnection * cnc)138 dump_vtables (GdaLdapConnection *cnc)
139 {
140 	GSList *list;
141 	g_print ("LDAP tables: %d\n", g_slist_length (cnc->priv->maps));
142 	for (list = cnc->priv->maps; list; list = list->next) {
143 		LdapTableMap *map = (LdapTableMap*) list->data;
144 		g_print ("    LDAP Vtable: %s (map %p)\n", map->table_name, map);
145 	}
146 }
147 #endif
148 
149 static void
vtable_created(GdaVconnectionDataModel * cnc,const gchar * table_name)150 vtable_created (GdaVconnectionDataModel *cnc, const gchar *table_name)
151 {
152 #ifdef GDA_DEBUG_NO
153 	g_print ("VTable created: %s\n", table_name);
154 	dump_vtables (GDA_LDAP_CONNECTION (cnc));
155 #endif
156 	if (GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_created)
157 		GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_created (cnc, table_name);
158 	update_connection_startup_file (GDA_LDAP_CONNECTION (cnc));
159 }
160 
161 static void
vtable_dropped(GdaVconnectionDataModel * cnc,const gchar * table_name)162 vtable_dropped (GdaVconnectionDataModel *cnc, const gchar *table_name)
163 {
164 	GdaLdapConnection *lcnc;
165 	LdapTableMap *map = NULL;
166 	GSList *list;
167 
168 	lcnc = GDA_LDAP_CONNECTION (cnc);
169 	for (list = lcnc->priv->maps; list; list = list->next) {
170 		if (!strcmp (((LdapTableMap*)list->data)->table_name, table_name)) {
171 			map = (LdapTableMap*)list->data;
172 			break;
173 		}
174 	}
175 	if (map) {
176 		lcnc->priv->maps = g_slist_remove (lcnc->priv->maps, map);
177 #ifdef GDA_DEBUG_NO
178 		g_print ("VTable dropped: %s\n", table_name);
179 		dump_vtables (lcnc);
180 #endif
181 	}
182 	if (GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_dropped)
183 		GDA_VCONNECTION_DATA_MODEL_CLASS (parent_class)->vtable_dropped (cnc, table_name);
184 	update_connection_startup_file (GDA_LDAP_CONNECTION (cnc));
185 }
186 
187 /*
188  * GdaLdapConnection class implementation
189  */
190 static void
gda_ldap_connection_class_init(GdaLdapConnectionClass * klass)191 gda_ldap_connection_class_init (GdaLdapConnectionClass *klass)
192 {
193 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
194 
195 	parent_class = g_type_class_peek_parent (klass);
196 	object_class->dispose = gda_ldap_connection_dispose;
197 	GDA_VCONNECTION_DATA_MODEL_CLASS (klass)->vtable_created = vtable_created;
198 	GDA_VCONNECTION_DATA_MODEL_CLASS (klass)->vtable_dropped = vtable_dropped;
199 
200 	/* Properties */
201         object_class->set_property = gda_ldap_connection_set_property;
202         object_class->get_property = gda_ldap_connection_get_property;
203 	g_object_class_install_property (object_class, PROP_STARTUP_FILE,
204                                          g_param_spec_string ("startup-file", NULL, _("File used to store startup data"), NULL,
205                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
206 }
207 
208 static void
dsn_set_cb(GdaLdapConnection * cnc,G_GNUC_UNUSED GParamSpec * pspec,G_GNUC_UNUSED gpointer data)209 dsn_set_cb (GdaLdapConnection *cnc, G_GNUC_UNUSED GParamSpec *pspec, G_GNUC_UNUSED gpointer data)
210 {
211 	gchar *fname, *tmp, *dsn;
212 	g_object_get (cnc, "dsn", &dsn, NULL);
213 	tmp = g_strdup_printf ("ldap-%s.start", dsn);
214 	g_free (dsn);
215 	fname = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
216 			      "libgda", tmp, NULL);
217 	g_free (tmp);
218 	g_free (cnc->priv->startup_file);
219 	cnc->priv->startup_file = fname;
220 }
221 
222 static void
conn_opened_cb(GdaLdapConnection * cnc,G_GNUC_UNUSED gpointer data)223 conn_opened_cb (GdaLdapConnection *cnc, G_GNUC_UNUSED gpointer data)
224 {
225 	if (!cnc->priv->startup_file)
226 		return;
227 
228 	cnc->priv->loading_startup_file = TRUE;
229 
230 	GdaSqlParser *parser;
231 	GdaBatch *batch;
232 	GError *lerror = NULL;
233 	parser = gda_connection_create_parser (GDA_CONNECTION (cnc));
234 	if (!parser)
235 		parser = gda_sql_parser_new ();
236 	batch = gda_sql_parser_parse_file_as_batch (parser, cnc->priv->startup_file, &lerror);
237 	if (batch) {
238 		GSList *list;
239 		list = gda_connection_batch_execute (GDA_CONNECTION (cnc), batch, NULL, 0, &lerror);
240 		g_slist_foreach (list, (GFunc) g_object_unref, NULL);
241 		g_slist_free (list);
242 		g_object_unref (batch);
243 	}
244 	if (lerror) {
245 		GdaConnectionEvent *event;
246 		gchar *msg;
247 		event = gda_connection_point_available_event (GDA_CONNECTION (cnc),
248 							      GDA_CONNECTION_EVENT_WARNING);
249 		msg = g_strdup_printf (_("Error recreating LDAP tables: %s"),
250 				       lerror && lerror->message ? lerror->message : _("No detail"));
251 		gda_connection_event_set_description (event, msg);
252 		gda_connection_add_event (GDA_CONNECTION (cnc), event);
253 		g_free (msg);
254 		g_clear_error (&lerror);
255 	}
256 	g_object_unref (parser);
257 
258 	cnc->priv->loading_startup_file = FALSE;
259 }
260 
261 static void
gda_ldap_connection_init(GdaLdapConnection * cnc,G_GNUC_UNUSED GdaLdapConnectionClass * klass)262 gda_ldap_connection_init (GdaLdapConnection *cnc, G_GNUC_UNUSED GdaLdapConnectionClass *klass)
263 {
264 	cnc->priv = g_new (GdaLdapConnectionPrivate, 1);
265 	cnc->priv->maps = NULL;
266 	cnc->priv->startup_file = NULL;
267 	cnc->priv->loading_startup_file = FALSE;
268 
269 	g_signal_connect (cnc, "notify::dsn",
270 			  G_CALLBACK (dsn_set_cb), NULL);
271 	g_signal_connect (cnc, "conn-opened",
272 			  G_CALLBACK (conn_opened_cb), NULL);
273 }
274 
275 static void
gda_ldap_connection_dispose(GObject * object)276 gda_ldap_connection_dispose (GObject *object)
277 {
278 	GdaLdapConnection *cnc = (GdaLdapConnection *) object;
279 
280 	g_return_if_fail (GDA_IS_LDAP_CONNECTION (cnc));
281 
282 	/* free memory */
283 	if (cnc->priv) {
284 		if (cnc->priv->maps)
285 			g_slist_free (cnc->priv->maps);
286 		g_free (cnc->priv->startup_file);
287 		g_free (cnc->priv);
288 		cnc->priv = NULL;
289 	}
290 
291 	/* chain to parent class */
292 	parent_class->dispose (object);
293 }
294 
295 GType
gda_ldap_connection_get_type(void)296 gda_ldap_connection_get_type (void)
297 {
298 	static GType type = 0;
299 
300 	if (G_UNLIKELY (type == 0)) {
301 		static GMutex registering;
302 		if (type == 0) {
303 			static GTypeInfo info = {
304 				sizeof (GdaLdapConnectionClass),
305 				(GBaseInitFunc) NULL,
306 				(GBaseFinalizeFunc) NULL,
307 				(GClassInitFunc) gda_ldap_connection_class_init,
308 				NULL, NULL,
309 				sizeof (GdaLdapConnection),
310 				0,
311 				(GInstanceInitFunc) gda_ldap_connection_init,
312 				0
313 			};
314 
315 		g_mutex_lock (&registering);
316 		if (type == 0)
317 			type = g_type_register_static (GDA_TYPE_VCONNECTION_DATA_MODEL, "GdaLdapConnection", &info, 0);
318 		g_mutex_unlock (&registering);
319 		}
320 	}
321 
322 	return type;
323 }
324 
325 static void
gda_ldap_connection_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)326 gda_ldap_connection_set_property (GObject *object,
327 				  guint param_id,
328 				  const GValue *value,
329 				  GParamSpec *pspec)
330 {
331         GdaLdapConnection *cnc;
332         cnc = GDA_LDAP_CONNECTION (object);
333         if (cnc->priv) {
334                 switch (param_id) {
335                 case PROP_STARTUP_FILE: {
336 			if (cnc->priv->startup_file) {
337 				/* don't override any preexisting setting from a DSN */
338 				gchar *dsn;
339 				g_object_get (cnc, "dsn", &dsn, NULL);
340 				if (dsn)
341 					g_free (dsn);
342 				else {
343 					g_free (cnc->priv->startup_file);
344 					cnc->priv->startup_file = NULL;
345 				}
346 			}
347 			if (! cnc->priv->startup_file) {
348 				if (g_value_get_string (value))
349 					cnc->priv->startup_file = g_value_dup_string (value);
350 			}
351 			break;
352 		}
353 		default:
354 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
355                         break;
356                 }
357 	}
358 }
359 
360 static void
gda_ldap_connection_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)361 gda_ldap_connection_get_property (GObject *object,
362 				  guint param_id,
363 				  GValue *value,
364 				  GParamSpec *pspec)
365 {
366 	GdaLdapConnection *cnc;
367         cnc = GDA_LDAP_CONNECTION (object);
368         if (cnc->priv) {
369                 switch (param_id) {
370                 case PROP_STARTUP_FILE:
371 			g_value_set_string (value, cnc->priv->startup_file);
372 			break;
373 		default:
374 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
375                         break;
376                 }
377 	}
378 }
379 
380 static GList *
_ldap_create_columns_func(GdaVconnectionDataModelSpec * spec,G_GNUC_UNUSED GError ** error)381 _ldap_create_columns_func (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED GError **error)
382 {
383 	LdapTableMap *map = (LdapTableMap *) spec;
384 	if (!map->columns)
385 		map->columns = gda_data_model_ldap_compute_columns (map->ldap_cnc, map->attributes);
386 	g_list_foreach (map->columns, (GFunc) g_object_ref, NULL);
387 	return g_list_copy (map->columns);
388 }
389 
390 static gchar *
make_string_for_filter(GdaVconnectionDataModelFilter * info)391 make_string_for_filter (GdaVconnectionDataModelFilter *info)
392 {
393 	GString *string;
394 	gint i;
395 
396 	string = g_string_new ("");
397 	for (i = 0; i < info->nConstraint; i++) {
398 		const struct GdaVirtualConstraint *cons;
399 		cons = &(info->aConstraint [i]);
400 		g_string_append_printf (string, "|%d,%d", cons->iColumn, cons->op);
401 	}
402 	return g_string_free (string, FALSE);
403 }
404 
405 typedef struct {
406 	gint dn_constindex; /* constraint number to set the DN from when actually executing the LDAP search, or -1 */
407 	gchar *ldap_filter; /* in LDAP format, with @xxx@ to bind values */
408 	struct GdaVirtualConstraintUsage *out_const;
409 } ComputedFilter;
410 
411 static void
computed_filter_free(ComputedFilter * filter)412 computed_filter_free (ComputedFilter *filter)
413 {
414 	g_free (filter->out_const);
415 	g_free (filter->ldap_filter);
416 	g_free (filter);
417 }
418 
419 #define MARKER_ESCAPE_CHAR 1
420 #define MARKER_GLOB_CHAR 2
421 static void
_ldap_table_create_filter(GdaVconnectionDataModelSpec * spec,GdaVconnectionDataModelFilter * info)422 _ldap_table_create_filter (GdaVconnectionDataModelSpec *spec, GdaVconnectionDataModelFilter *info)
423 {
424 	/*
425 	 * REM:
426 	 *   - LDAP does not allow filtering on the DN => filter only is some cases
427 	 *   - LDAP does not handle ordering of results => the 'ORDER BY' constraint is ignored
428 	 *   - the '>' and '<' operators are not allowed in search strings => using '>=' and '<=' and
429 	 *     have SQLite do the actual check
430 	 */
431 	LdapTableMap *map = (LdapTableMap *) spec;
432 	GString *filter_string = NULL;
433 	gint i, ncols;
434 	gint dn_constindex = -1;
435 	gchar *hash;
436 
437 	info->orderByConsumed = FALSE;
438 	hash = make_string_for_filter (info);
439 	if (map->filters_hash) {
440 		ComputedFilter *filter;
441 		filter = g_hash_table_lookup (map->filters_hash, hash);
442 		if (filter) {
443 			info->idxPointer = (gpointer) filter;
444 			info->orderByConsumed = FALSE;
445 			memcpy (info->aConstraintUsage,
446 				filter->out_const,
447 				sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint);
448 			/*g_print ("Reusing filter %p, hash=[%s]\n", filter, hash);*/
449 			g_free (hash);
450 			return;
451 		}
452 	}
453 
454 	if (!map->columns)
455 		map->columns = gda_data_model_ldap_compute_columns (map->ldap_cnc, map->attributes);
456 
457 	ncols = g_list_length (map->columns);
458 	for (i = 0; i < info->nConstraint; i++) {
459 		const struct GdaVirtualConstraint *cons;
460 		cons = &(info->aConstraint [i]);
461 		const gchar *attrname;
462 
463 		info->aConstraintUsage[i].argvIndex = i+1;
464 		info->aConstraintUsage[i].omit = TRUE;
465 
466 		if (cons->iColumn < 0) {
467 			g_warning ("Internal error: negative column number!");
468 			goto nofilter;
469 		}
470 		if (cons->iColumn >= ncols) {
471 			g_warning ("Internal error: SQLite's virtual table column %d is not known for "
472 				   "table '%s', which has %d column(s)", cons->iColumn, map->table_name,
473 				   ncols);
474 			goto nofilter;
475 		}
476 		if (cons->iColumn == 0) {
477 			/* try to optimize on the DN column */
478 			if ((map->scope == GDA_LDAP_SEARCH_BASE) ||
479 			    (map->scope == GDA_LDAP_SEARCH_ONELEVEL))
480 				goto nofilter;
481 			if (cons->op != GDA_SQL_OPERATOR_TYPE_EQ)
482 				goto nofilter;
483 			if (dn_constindex != -1) /* DN is already filtered by a constraint */
484 				goto nofilter;
485 			dn_constindex = i;
486 			continue;
487 		}
488 
489 		attrname = gda_column_get_name (GDA_COLUMN (g_list_nth_data (map->columns, cons->iColumn)));
490 		if (! filter_string) {
491 			if ((info->nConstraint > 1) || map->filter)
492 				filter_string = g_string_new ("(& ");
493 			else
494 				filter_string = g_string_new ("");
495 			if (map->filter)
496 				g_string_append (filter_string, map->filter);
497 		}
498 		switch (cons->op) {
499 		case GDA_SQL_OPERATOR_TYPE_EQ:
500 			g_string_append_printf (filter_string, "(%s=%c)", attrname, MARKER_ESCAPE_CHAR);
501 			break;
502 		case GDA_SQL_OPERATOR_TYPE_GT:
503 			g_string_append_printf (filter_string, "(%s>=%c)", attrname, MARKER_ESCAPE_CHAR);
504 			info->aConstraintUsage[i].omit = FALSE;
505 			break;
506 		case GDA_SQL_OPERATOR_TYPE_LEQ:
507 			g_string_append_printf (filter_string, "(%s<=%c)", attrname, MARKER_ESCAPE_CHAR);
508 			info->aConstraintUsage[i].omit = FALSE;
509 			break;
510 		case GDA_SQL_OPERATOR_TYPE_LT:
511 			g_string_append_printf (filter_string, "(%s<=%c)", attrname, MARKER_ESCAPE_CHAR);
512 			break;
513 		case GDA_SQL_OPERATOR_TYPE_GEQ:
514 			g_string_append_printf (filter_string, "(%s>=%c)", attrname, MARKER_ESCAPE_CHAR);
515 			break;
516 		case GDA_SQL_OPERATOR_TYPE_REGEXP:
517 			g_string_append_printf (filter_string, "(%s=%c)", attrname, MARKER_GLOB_CHAR);
518 			break;
519 		default:
520 			/* Can't be done with LDAP */
521 			goto nofilter;
522 		}
523 	}
524 
525 	if (!filter_string && (dn_constindex == -1))
526 		goto nofilter;
527 
528 	if (filter_string && ((info->nConstraint > 1) || map->filter))
529 		g_string_append_c (filter_string, ')');
530 	/*g_print ("FILTER: [%s]\n", filter_string->str);*/
531 
532 	ComputedFilter *filter;
533 	filter = g_new0 (ComputedFilter, 1);
534 	filter->dn_constindex = dn_constindex;
535 	if (filter_string)
536 		filter->ldap_filter = g_string_free (filter_string, FALSE);
537 	else if (map->filter)
538 		filter->ldap_filter = g_strdup (map->filter);
539 	filter->out_const = g_new (struct GdaVirtualConstraintUsage,  info->nConstraint);
540 	memcpy (filter->out_const,
541 		info->aConstraintUsage,
542 		sizeof (struct GdaVirtualConstraintUsage) * info->nConstraint);
543 
544 	info->idxPointer = (gpointer) filter;
545 	if (! map->filters_hash)
546 		map->filters_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
547 							   g_free,
548 							   (GDestroyNotify) computed_filter_free);
549 	g_hash_table_insert (map->filters_hash, hash, filter);
550 	/*g_print ("There are now %d statements in store...\n", g_hash_table_size (map->filters_hash));*/
551 	return;
552 
553  nofilter:
554 	if (filter_string)
555 		g_string_free (filter_string, TRUE);
556 	for (i = 0; i < info->nConstraint; i++) {
557 		info->aConstraintUsage[i].argvIndex = 0;
558 		info->aConstraintUsage[i].omit = TRUE;
559 	}
560 	info->idxPointer = NULL;
561 }
562 
563 static GdaDataModel *
_ldap_table_create_model_func(GdaVconnectionDataModelSpec * spec,G_GNUC_UNUSED int idxNum,const char * idxStr,int argc,GValue ** argv)564 _ldap_table_create_model_func (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED int idxNum, const char *idxStr,
565 			       int argc, GValue **argv)
566 {
567 	LdapTableMap *map = (LdapTableMap *) spec;
568 	GdaDataModel *model;
569 
570 	if (idxStr) {
571 		const gchar *ptr;
572 		gint pos;
573 		GString *real_filter = NULL;
574 		ComputedFilter *filter = (ComputedFilter *) idxStr;
575 
576 		for (pos = 0, ptr = filter->ldap_filter ? filter->ldap_filter : ""; *ptr; ptr++) {
577 			if (pos == filter->dn_constindex)
578 				pos++; /* skip this constraint position */
579 			if (! real_filter)
580 				real_filter = g_string_new ("");
581 			if ((*ptr == MARKER_ESCAPE_CHAR) || (*ptr == MARKER_GLOB_CHAR)){
582 				gchar *str, *sptr;
583 				gchar marker;
584 				marker = *ptr;
585 				g_assert (pos < argc);
586 				str = gda_value_stringify (argv[pos]);
587 				for (sptr = str; *sptr; sptr++) {
588 					if ((*sptr == ')') || (*sptr == '(') || (*sptr == '\\') || (*sptr == '*'))
589 						break;
590 					if ((marker == MARKER_GLOB_CHAR) && (*sptr == '%'))
591 						break;
592 				}
593 				if (*sptr) {
594 					/* make the substitutions */
595 					GString *string;
596 					string = g_string_new ("");
597 					for (sptr = str; *sptr; sptr++) {
598 						if (*sptr == ')')
599 							g_string_append (string, "\\29");
600 						else if (*sptr == '(')
601 							g_string_append (string, "\\28");
602 						else if (*sptr == '\\')
603 							g_string_append (string, "\\5c");
604 						else if (*sptr == '*')
605 							g_string_append (string, "\\2a");
606 						else if ((marker == MARKER_GLOB_CHAR) && (*sptr == '%'))
607 							g_string_append_c (string, '*');
608 						else
609 							g_string_append_c (string, *sptr);
610 					}
611 					g_free (str);
612 					str = g_string_free (string, FALSE);
613 				}
614 				g_string_append (real_filter, str);
615 				g_free (str);
616 				pos++;
617 			}
618 			else
619 				g_string_append_c (real_filter, *ptr);
620 		}
621 
622 		gchar *real_dn = NULL;
623 		GdaLdapSearchScope real_scope = map->scope;
624 		if (filter->dn_constindex != -1) {
625 			/* check that the DN is a child of the data model's base DN */
626 			const gchar *bdn;
627 			gchar *tmp;
628 			bdn = map->base_dn;
629 			if (!bdn)
630 				bdn = gda_ldap_connection_get_base_dn (GDA_LDAP_CONNECTION (map->ldap_cnc));
631 			g_assert (bdn);
632 			tmp = gda_value_stringify (argv[filter->dn_constindex]);
633 			if (g_str_has_suffix (tmp, bdn)) {
634 				real_scope = GDA_LDAP_SEARCH_BASE;
635 				real_dn = gda_value_stringify (argv[filter->dn_constindex]);
636 			}
637 			else {
638 				/* return empty set */
639 				if (real_filter)
640 					g_string_free (real_filter, TRUE);
641 				real_filter = g_string_new ("(objectClass=)");
642 			}
643 			g_free (tmp);
644 		}
645 
646 		/*g_print ("FILTER to use: LDAPFilter=> [%s] LDAPDn => [%s] SCOPE => [%d]\n",
647 			 real_filter ? real_filter->str : NULL,
648 			 real_dn ? real_dn : map->base_dn, real_scope);*/
649 		model = gda_data_model_ldap_new (map->ldap_cnc,
650 						 real_dn ? real_dn : map->base_dn,
651 						 real_filter ? real_filter->str : NULL,
652 						 map->attributes, real_scope);
653 		if (real_filter)
654 			g_string_free (real_filter, TRUE);
655 		g_free (real_dn);
656 	}
657 	else
658 		model = gda_data_model_ldap_new (map->ldap_cnc, map->base_dn, map->filter,
659 						 map->attributes, map->scope);
660 
661 	return model;
662 }
663 
664 /**
665  * gda_ldap_connection_declare_table:
666  * @cnc: a #GdaLdapConnection
667  * @table_name: a table name, not %NULL
668  * @base_dn: (allow-none): the base DN of the LDAP search, or %NULL for @cnc's own base DN
669  * @filter: (allow-none): the search filter of the LDAP search, or %NULL for a default filter of "(ObjectClass=*)"
670  * @attributes: (allow-none): the search attributes of the LDAP search, or %NULL if only the DN is required
671  * @scope: the search scope of the LDAP search
672  * @error: a place to store errors, or %NULL
673  *
674  * Declare a virtual table based on an LDAP search.
675  *
676  * The @filter argument, if not %NULL, must be a valid LDAP filter string (including the opening and
677  * closing parenthesis).
678  *
679  * The @attribute, if not %NULL, is a list of comma separated LDAP entry attribute names. For each attribute
680  * it is also possible to specify a requested #GType, and how to behave in case of multi valued attributes
681  * So the general format for an attribute is:
682  * "&lt;attribute name&gt;[::&lt;type&gt;][::&lt;muti value handler&gt;]", where:
683  * <itemizedlist>
684  * <listitem><para>"::&lt;type&gt;" is optional, see gda_g_type_from_string() for more
685  *     information about valie values for &lt;type&gt;</para></listitem>
686  * <listitem><para>"::&lt;muti value handler&gt;" is also optional and specifies how a multi
687  *    values attributed is treated. The possibilities for &lt;muti value handler&gt; are:
688  *    <itemizedlist>
689  *         <listitem><para>"NULL" or "0": a NULL value will be returned</para></listitem>
690  *         <listitem><para>"CSV": a comma separated value with all the values of the attribute will be
691  *           returned. This only works for G_TYPE_STRING attribute types.</para></listitem>
692  *         <listitem><para>"MULT" of "*": a row will be returned for each value of the attribute, effectively
693  *           multiplying the number of returned rows</para></listitem>
694  *         <listitem><para>"1": only the first vakue of the attribute will be used, the other values ignored</para></listitem>
695  *         <listitem><para>"CONCAT": the attributes' values are concatenated (with a newline char between each value)</para></listitem>
696  *         <listitem><para>"ERROR": an error value will be returned (this is the default behaviour)</para></listitem>
697  *    </itemizedlist>
698  * </para></listitem>
699  * </itemizedlist>
700  *
701  *  After each attribute
702  * name (and before the comma for the next attribute if any), it is possible to specify the #GType type using
703  * the "::&lt;type&gt;" syntax (see gda_g_type_from_string() for more information).
704  *
705  * The following example specifies the "uidNumber" attribute to be returned as a string, the "mail" attribute,
706  * and the "objectClass" attribute to be handled as one row per value of that attribute:
707  * <programlisting>"uidNumber::string,mail,objectClass::*"</programlisting>
708  *
709  * Returns: %TRUE if no error occurred
710  *
711  * Since: 4.2.8
712  */
713 gboolean
gda_ldap_connection_declare_table(GdaLdapConnection * cnc,const gchar * table_name,const gchar * base_dn,const gchar * filter,const gchar * attributes,GdaLdapSearchScope scope,GError ** error)714 gda_ldap_connection_declare_table (GdaLdapConnection *cnc, const gchar *table_name,
715 				   const gchar *base_dn, const gchar *filter,
716 				   const gchar *attributes, GdaLdapSearchScope scope,
717 				   GError **error)
718 {
719 	LdapTableMap *map;
720 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
721 	g_return_val_if_fail (table_name && *table_name, FALSE);
722 
723 	map = g_new0 (LdapTableMap, 1);
724 	GDA_VCONNECTION_DATA_MODEL_SPEC (map)->data_model = NULL;
725         GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_columns_func = (GdaVconnectionDataModelCreateColumnsFunc) _ldap_create_columns_func;
726         GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_model_func = NULL;
727         GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_filter_func = _ldap_table_create_filter;
728         GDA_VCONNECTION_DATA_MODEL_SPEC (map)->create_filtered_model_func = _ldap_table_create_model_func;
729 	map->ldap_cnc = g_object_ref (cnc);
730 	map->table_name = gda_sql_identifier_quote (table_name, GDA_CONNECTION (cnc), NULL, TRUE, FALSE);
731 	map->filters_hash = NULL;
732 	if (base_dn)
733 		map->base_dn = g_strdup (base_dn);
734 	if (filter)
735 		map->filter = g_strdup (filter);
736 	if (attributes)
737 		map->attributes = g_strdup (attributes);
738 	map->scope = scope ? scope : GDA_LDAP_SEARCH_BASE;
739 
740 	cnc->priv->maps = g_slist_append (cnc->priv->maps, map);
741 	if (!gda_vconnection_data_model_add (GDA_VCONNECTION_DATA_MODEL (cnc),
742 					     (GdaVconnectionDataModelSpec*) map,
743                                              (GDestroyNotify) _ldap_table_map_free, table_name, error)) {
744 		cnc->priv->maps = g_slist_remove (cnc->priv->maps, map);
745                 return FALSE;
746 	}
747 
748 	return TRUE;
749 }
750 
751 /**
752  * gda_ldap_connection_undeclare_table:
753  * @cnc: a #GdaLdapConnection
754  * @table_name: a table name, not %NULL
755  * @error: a place to store errors, or %NULL
756  *
757  * Remove a table which has been declared using gda_ldap_connection_declare_table().
758  *
759  * Returns: %TRUE if no error occurred
760  *
761  * Since: 4.2.8
762  */
763 gboolean
gda_ldap_connection_undeclare_table(GdaLdapConnection * cnc,const gchar * table_name,GError ** error)764 gda_ldap_connection_undeclare_table (GdaLdapConnection *cnc, const gchar *table_name, GError **error)
765 {
766 	GdaVconnectionDataModelSpec *specs;
767 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
768 	g_return_val_if_fail (table_name && *table_name, FALSE);
769 
770 	specs =  gda_vconnection_data_model_get (GDA_VCONNECTION_DATA_MODEL (cnc), table_name);
771 	if (specs && ! g_slist_find (cnc->priv->maps, specs)) {
772 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
773 			     GDA_SERVER_PROVIDER_MISUSE_ERROR,
774 			     "%s", _("Can't remove non LDAP virtual table"));
775 		return FALSE;
776 	}
777 	return gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (cnc), table_name, error);
778 }
779 
780 /**
781  * gda_ldap_connection_describe_table:
782  * @cnc: a #GdaLdapConnection
783  * @table_name: a table name, not %NULL
784  * @out_base_dn: (allow-none) (transfer none): a place to store the LDAP search base DN, or %NULL
785  * @out_filter: (allow-none) (transfer none): a place to store the LDAP search filter, or %NULL
786  * @out_attributes: (allow-none) (transfer none): a place to store the LDAP search attributes, or %NULL
787  * @out_scope: (allow-none) (transfer none): a place to store the LDAP search scope, or %NULL
788  * @error: a place to store errors, or %NULL
789  *
790  * Get information about a virtual table, the information which has been passed to
791  * gda_ldap_connection_declare_table() when the table was created.
792  *
793  * Returns: %TRUE if no error occurred
794  *
795  * Since: 4.2.8
796  */
797 gboolean
gda_ldap_connection_describe_table(GdaLdapConnection * cnc,const gchar * table_name,const gchar ** out_base_dn,const gchar ** out_filter,const gchar ** out_attributes,GdaLdapSearchScope * out_scope,GError ** error)798 gda_ldap_connection_describe_table (GdaLdapConnection *cnc, const gchar *table_name,
799 				    const gchar **out_base_dn, const gchar **out_filter,
800 				    const gchar **out_attributes,
801 				    GdaLdapSearchScope *out_scope, GError **error)
802 {
803 	GdaVconnectionDataModelSpec *specs;
804 	LdapTableMap *map;
805 
806 	if (out_base_dn)
807 		*out_base_dn = NULL;
808 	if (out_filter)
809 		*out_filter = NULL;
810 	if (out_attributes)
811 		*out_attributes = NULL;
812 	if (out_scope)
813 		*out_scope = GDA_LDAP_SEARCH_BASE;
814 
815 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
816 	g_return_val_if_fail (table_name && *table_name, FALSE);
817 
818 	specs =  gda_vconnection_data_model_get (GDA_VCONNECTION_DATA_MODEL (cnc), table_name);
819 	if (specs && ! g_slist_find (cnc->priv->maps, specs)) {
820 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
821 			     GDA_SERVER_PROVIDER_MISUSE_ERROR,
822 			     "%s", _("Can't describe non LDAP virtual table"));
823 		return FALSE;
824 	}
825 
826 	if (!specs) {
827 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
828 			     GDA_SERVER_PROVIDER_MISUSE_ERROR,
829 			     "%s", _("Unknown LDAP virtual table"));
830 		return FALSE;
831 	}
832 
833 	map = (LdapTableMap*) specs;
834 	if (out_base_dn)
835 		*out_base_dn = map->base_dn;
836 	if (out_filter)
837 		*out_filter = map->filter;
838 	if (out_attributes)
839 		*out_attributes = map->attributes;
840 	if (out_scope)
841 		*out_scope = map->scope;
842 	return TRUE;
843 }
844 
845 static void
gda_ldap_attribute_free(GdaLdapAttribute * attr)846 gda_ldap_attribute_free (GdaLdapAttribute *attr)
847 {
848 	if (attr) {
849 		gint i;
850 		g_free (attr->attr_name);
851 		for (i = 0; attr->values[i]; i++)
852 			gda_value_free (attr->values[i]);
853 		g_free (attr->values);
854 	}
855 }
856 
857 /**
858  * gda_ldap_entry_free:
859  * @entry: (transfer full): a #GdaLdapEntry pointer
860  *
861  * Frees @entry
862  *
863  * Since: 4.2.8
864  */
865 void
gda_ldap_entry_free(GdaLdapEntry * entry)866 gda_ldap_entry_free (GdaLdapEntry *entry)
867 {
868 	if (entry) {
869 		g_free (entry->dn);
870 		if (entry->attributes) {
871 			gint i;
872 			for (i = 0; entry->attributes[i]; i++)
873 				gda_ldap_attribute_free (entry->attributes[i]);
874 			g_free (entry->attributes);
875 		}
876 		if (entry->attributes_hash)
877 			g_hash_table_destroy (entry->attributes_hash);
878 		g_free (entry);
879 	}
880 }
881 
882 /**
883  * gda_ldap_entry_new:
884  * @dn: (allow-none): a Distinguished name, or %NULL
885  *
886  * Creates a new #GdaLdapEntry. This function is useful when using gda_ldap_modify_entry()
887  *
888  * Returns: a new #GdaLdapEntry
889  *
890  * Since: 5.2.0
891  */
892 GdaLdapEntry *
gda_ldap_entry_new(const gchar * dn)893 gda_ldap_entry_new (const gchar *dn)
894 {
895 	GdaLdapEntry *entry;
896 	entry = g_new0 (GdaLdapEntry, 1);
897 	if (dn)
898 		entry->dn = g_strdup (dn);
899 	entry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
900 	entry->nb_attributes = 0;
901 	entry->attributes = g_new0 (GdaLdapAttribute*, 1);
902 	return entry;
903 }
904 
905 /**
906  * gda_ldap_entry_add_attribute:
907  * @entry: a #GdaLdapEntry pointer
908  * @merge: set to %TRUE to merge the values in case of an existing attribute in @entry, and %FALSE to replace any existing attribute's values in @entry
909  * @attr_name: the name of the attribute to add
910  * @nb_values: number of values in @values
911  * @values: (array length=nb_values): an array of #GValue (as much values as specified by @nb_values)
912  *
913  * Add an attribute (ans its values) to @entry. If the attribute is already present in @entry,
914  * then the attribute's values are merged or replaced depending on the @merge argument.
915  *
916  * Since: 5.2.0
917  */
918 void
gda_ldap_entry_add_attribute(GdaLdapEntry * entry,gboolean merge,const gchar * attr_name,guint nb_values,GValue ** values)919 gda_ldap_entry_add_attribute (GdaLdapEntry *entry, gboolean merge, const gchar *attr_name,
920 			      guint nb_values, GValue **values)
921 {
922 	guint i;
923 	g_return_if_fail (entry);
924 	g_return_if_fail (nb_values > 0);
925 	g_return_if_fail (values);
926 	g_return_if_fail (attr_name && *attr_name);
927 
928 	GdaLdapAttribute *att = NULL;
929 	GdaLdapAttribute *natt = NULL;
930 	guint replace_pos = G_MAXUINT;
931 
932 	if (entry->attributes_hash)
933 		att = g_hash_table_lookup (entry->attributes_hash, attr_name);
934 	else
935 		entry->attributes_hash = g_hash_table_new (g_str_hash, g_str_equal);
936 
937 	if (att) {
938 		if (merge) {
939 			TO_IMPLEMENT;
940 			return;
941 		}
942 		else {
943 			/* get rid of @attr */
944 			g_hash_table_remove (entry->attributes_hash, att->attr_name);
945 			for (i = 0; i < entry->nb_attributes; i++) {
946 				if (entry->attributes [i] == att) {
947 					replace_pos = i;
948 					entry->attributes [i] = NULL;
949 					break;
950 				}
951 			}
952 			gda_ldap_attribute_free (att);
953 		}
954 	}
955 
956 	natt = g_new0 (GdaLdapAttribute, 1);
957 	natt->attr_name = g_strdup (attr_name);
958 	natt->nb_values = nb_values;
959 	if (natt->nb_values > 0) {
960 		natt->values = g_new0 (GValue *, natt->nb_values + 1);
961 		for (i = 0; i < natt->nb_values; i++) {
962 			if (values [i])
963 				natt->values [i] = gda_value_copy (values [i]);
964 			else
965 				natt->values [i] = NULL;
966 		}
967 	}
968 	g_hash_table_insert (entry->attributes_hash, natt->attr_name, natt);
969 	if (replace_pos != G_MAXUINT)
970 		entry->attributes [replace_pos] = natt;
971 	else {
972 		entry->nb_attributes++;
973 		entry->attributes = g_renew (GdaLdapAttribute*, entry->attributes, entry->nb_attributes + 1);
974 		entry->attributes [entry->nb_attributes - 1] = natt;
975 		entry->attributes [entry->nb_attributes] = NULL;
976 	}
977 }
978 
979 
980 /* proxy declaration */
981 GdaLdapEntry *_gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error);
982 GdaLdapEntry **_gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error);
983 
984 /**
985  * gda_ldap_describe_entry:
986  * @cnc: a #GdaLdapConnection connection
987  * @dn: (allow-none): the Distinguished Name of the LDAP entry to describe
988  * @error: (allow-none): a place to store errors, or %NULL
989  *
990  * Describes the LDAP entry which DN is @dn. If @dn is %NULL, then the top entry (as specified when
991  * the LDAP connection was opened) is described.
992  *
993  * Returns: (transfer full) (Free-function: gda_ldap_entry_free): a new #GdaLdapEntry, or %NULL if an error occurred or if the @dn entry does not exist
994  *
995  * Since: 4.2.8
996  */
997 GdaLdapEntry *
gda_ldap_describe_entry(GdaLdapConnection * cnc,const gchar * dn,GError ** error)998 gda_ldap_describe_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
999 {
1000 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1001 
1002 	return _gda_ldap_describe_entry (cnc, dn, error);
1003 }
1004 
1005 /**
1006  * gda_ldap_get_entry_children:
1007  * @cnc: a #GdaLdapConnection connection
1008  * @dn: (allow-none): the Distinguished Name of the LDAP entry to get children from
1009  * @attributes: (allow-none) (array zero-terminated=1) (element-type gchar*): a %NULL terminated array of attributes to fetch for each child, or %NULL if no attribute is requested
1010  * @error: (allow-none): a place to store errors, or %NULL
1011  *
1012  * Get the list of children entries for the LDAP entry which DN is @dn. If the @dn entry does not have any
1013  * child, then this function returns an array which first element is %NULL.
1014  *
1015  * If @dn is %NULL, then the top entry (as specified when the LDAP connection was opened) is used.
1016  *
1017  * Returns: (transfer full) (element_type GdaLdapEntry) (array zero-terminated=1): a %NULL terminated array of #GdaLdapEntry for each child entry, or %NULL if an error occurred or if the @dn entry does not exist
1018  *
1019  * Since: 4.2.8
1020  */
1021 GdaLdapEntry **
gda_ldap_get_entry_children(GdaLdapConnection * cnc,const gchar * dn,gchar ** attributes,GError ** error)1022 gda_ldap_get_entry_children (GdaLdapConnection *cnc, const gchar *dn, gchar **attributes, GError **error)
1023 {
1024 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1025 
1026 	return _gda_ldap_get_entry_children (cnc, dn, attributes, error);
1027 }
1028 
1029 gchar **_gda_ldap_dn_split (const gchar *dn, gboolean all);
1030 
1031 /**
1032  * gda_ldap_dn_split
1033  * @dn: a Distinguished Name string
1034  * @all: set to %FALSE to split @dn into its RND and its parent DN, or %TRUE to completely split @dn
1035  *
1036  * Splits @dn into its components.
1037  *
1038  * Returns: (transfer full) (Free-function: g_strfreev): a %NULL terminated array containing the DN parts (free using g_strfreev()), or %NULL if an error occurred because @dn is not a valid DN expression
1039  *
1040  * Since: 4.2.8
1041  */
1042 gchar **
gda_ldap_dn_split(const gchar * dn,gboolean all)1043 gda_ldap_dn_split (const gchar *dn, gboolean all)
1044 {
1045 	return _gda_ldap_dn_split (dn, all);
1046 }
1047 
1048 gboolean _gda_ldap_is_dn (const gchar *dn);
1049 
1050 /**
1051  * gda_ldap_is_dn:
1052  * @dn: a Distinguished Name string
1053  *
1054  * Tells if @dn represents a distinguished name (it only checks for the syntax, not for
1055  * the actual existence of the entry with that distinguished name).
1056  *
1057  * Returns: %TRUE if @dn is a valid representation of a distinguished name
1058  *
1059  * Since: 4.2.8
1060  */
1061 gboolean
gda_ldap_is_dn(const gchar * dn)1062 gda_ldap_is_dn (const gchar *dn)
1063 {
1064 	return _gda_ldap_is_dn (dn);
1065 }
1066 
1067 const gchar *_gda_ldap_get_base_dn (GdaLdapConnection *cnc);
1068 
1069 /**
1070  * gda_ldap_connection_get_base_dn:
1071  * @cnc: a #GdaLdapConnection
1072  *
1073  * Get the base DN which was used when the LDAP connection was opened
1074  *
1075  * Returns: the base DN, or %NULL
1076  *
1077  * Since: 4.2.8
1078  */
1079 const gchar *
gda_ldap_connection_get_base_dn(GdaLdapConnection * cnc)1080 gda_ldap_connection_get_base_dn (GdaLdapConnection *cnc)
1081 {
1082 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
1083 
1084 	return _gda_ldap_get_base_dn (cnc);
1085 }
1086 
1087 GdaLdapClass *_gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname);
1088 /**
1089  * gda_ldap_get_class_info:
1090  * @cnc: a #GdaLdapConnection
1091  * @classname: an LDAP class name
1092  *
1093  * Get information about an LDAP class
1094  *
1095  * Returns: (transfer none): a #GdaLdapClass
1096  *
1097  * Since: 4.2.8
1098  */
1099 GdaLdapClass*
gda_ldap_get_class_info(GdaLdapConnection * cnc,const gchar * classname)1100 gda_ldap_get_class_info (GdaLdapConnection *cnc, const gchar *classname)
1101 {
1102 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
1103 
1104 	return _gda_ldap_get_class_info (cnc, classname);
1105 }
1106 
1107 const GSList *_gda_ldap_get_top_classes (GdaLdapConnection *cnc);
1108 /**
1109  * gda_ldap_get_top_classes:
1110  * @cnc: a #GdaLdapConnection
1111  *
1112  * get a list of the top level LDAP classes (ie. classes which don't have any parent)
1113  *
1114  * Returns: (transfer none) (element-type GdaLdapClass): a list of #GdaLdapClass pointers (don't modify it)
1115  *
1116  * Since: 4.2.8
1117  */
1118 const GSList *
gda_ldap_get_top_classes(GdaLdapConnection * cnc)1119 gda_ldap_get_top_classes (GdaLdapConnection *cnc)
1120 {
1121 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), NULL);
1122 
1123 	return _gda_ldap_get_top_classes (cnc);
1124 }
1125 
1126 GSList *_gda_ldap_entry_get_attributes_list (GdaLdapConnection *cnc, GdaLdapEntry *entry, GdaLdapAttribute *object_class_attr);
1127 /**
1128  * gda_ldap_entry_get_attributes_list:
1129  * @cnc: a #GdaLdapConnection
1130  * @entry: a #GdaLdapEntry
1131  *
1132  * Get a list of all the possible attributes which @entry can have. Each possible attribute is represented
1133  * by a #GdaLdapAttributeDefinition strunture.
1134  *
1135  * Returns: (transfer full) (element-type GdaLdapAttributeDefinition): a #GSList of #GdaLdapAttributeDefinition pointers, free the list using gda_ldap_attributes_list_free()
1136  *
1137  * Since: 5.2.0
1138  */
1139 GSList *
gda_ldap_entry_get_attributes_list(GdaLdapConnection * cnc,GdaLdapEntry * entry)1140 gda_ldap_entry_get_attributes_list (GdaLdapConnection *cnc, GdaLdapEntry *entry)
1141 {
1142 	g_return_val_if_fail (entry, NULL);
1143 
1144 	return _gda_ldap_entry_get_attributes_list (cnc, entry, NULL);
1145 }
1146 
1147 /**
1148  * gda_ldap_attributes_list_free:
1149  * @list: (allow-none): a #GSList of #GdaLdapAttributeDefinition pointers, or %NULL
1150  *
1151  * Frees the list returned by gda_ldap_entry_get_attributes_list().
1152  *
1153  * Since: 5.2.0
1154  */
1155 void
gda_ldap_attributes_list_free(GSList * list)1156 gda_ldap_attributes_list_free (GSList *list)
1157 {
1158 	GSList *l;
1159 	if (!list)
1160 		return;
1161 	for (l = list; l; l = l->next) {
1162 		GdaLdapAttributeDefinition *def;
1163 		def = (GdaLdapAttributeDefinition*) l->data;
1164 		if (def) {
1165 			g_free (def->name);
1166 			g_free (def);
1167 		}
1168 	}
1169 	g_slist_free (list);
1170 }
1171 
1172 
1173 gboolean
1174 _gda_ldap_modify (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
1175 		  GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error);
1176 
1177 /**
1178  * gda_ldap_add_entry:
1179  * @cnc: a #GdaLdapConnection
1180  * @entry: a #GdaLDapEntry describing the LDAP entry to add
1181  * @error: (allow-none): a place to store an error, or %NULL
1182  *
1183  * Creates a new LDAP entry.
1184  *
1185  * Returns: %TRUE if no error occurred
1186  *
1187  * Since: 5.2.0
1188  */
1189 gboolean
gda_ldap_add_entry(GdaLdapConnection * cnc,GdaLdapEntry * entry,GError ** error)1190 gda_ldap_add_entry (GdaLdapConnection *cnc, GdaLdapEntry *entry, GError **error)
1191 {
1192 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1193 	g_return_val_if_fail (entry, FALSE);
1194 	g_return_val_if_fail (entry->dn && *(entry->dn), FALSE);
1195 
1196 	return _gda_ldap_modify (cnc, GDA_LDAP_MODIFICATION_INSERT, entry, NULL, error);
1197 }
1198 
1199 /**
1200  * gda_ldap_remove_entry:
1201  * @cnc: a #GdaLdapConnection
1202  * @entry: a #GdaLDapEntry describing the LDAP entry to remove
1203  * @error: (allow-none): a place to store an error, or %NULL
1204  *
1205  * Delete an LDAP entry.
1206  *
1207  * Returns: %TRUE if no error occurred
1208  *
1209  * Since: 5.2.0
1210  */
1211 gboolean
gda_ldap_remove_entry(GdaLdapConnection * cnc,const gchar * dn,GError ** error)1212 gda_ldap_remove_entry (GdaLdapConnection *cnc, const gchar *dn, GError **error)
1213 {
1214 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1215 	g_return_val_if_fail (dn && *dn, FALSE);
1216 
1217 	GdaLdapEntry entry;
1218 	memset (&entry, 0, sizeof (GdaLdapEntry));
1219 	entry.dn = (gchar*) dn;
1220 
1221 	return _gda_ldap_modify (cnc, GDA_LDAP_MODIFICATION_DELETE, &entry, NULL, error);
1222 }
1223 
1224 gboolean
1225 _gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error);
1226 
1227 /**
1228  * gda_ldap_rename_entry:
1229  * @cnc: a #GdaLdapConnection
1230  * @current_dn: the current DN of the entry
1231  * @new_dn: the new DN of the entry
1232  * @error: (allow-none): a place to store an error, or %NULL
1233  *
1234  * Renames an LDAP entry.
1235  *
1236  * Returns: %TRUE if no error occurred
1237  *
1238  * Since: 5.2.0
1239  */
1240 gboolean
gda_ldap_rename_entry(GdaLdapConnection * cnc,const gchar * current_dn,const gchar * new_dn,GError ** error)1241 gda_ldap_rename_entry (GdaLdapConnection *cnc, const gchar *current_dn, const gchar *new_dn, GError **error)
1242 {
1243 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1244 	g_return_val_if_fail (current_dn && *current_dn, FALSE);
1245 	g_return_val_if_fail (new_dn && *new_dn, FALSE);
1246 
1247 	return _gda_ldap_rename_entry (cnc, current_dn, new_dn, error);
1248 }
1249 
1250 /**
1251  * gda_ldap_modify_entry:
1252  * @cnc: a #GdaLdapConnection
1253  * @modtype: the type of modification to perform
1254  * @entry: a #GdaLDapEntry describing the LDAP entry to apply modifications, along with the attributes which will be modified
1255  * @ref_entry: (allow-none): a #GdaLDapEntry describing the reference LDAP entry, if @modtype is %GDA_LDAP_MODIFICATION_ATTR_DIFF
1256  * @error: (allow-none): a place to store an error, or %NULL
1257  *
1258  * Modifies an LDAP entry.
1259  *
1260  * Returns: %TRUE if no error occurred
1261  *
1262  * Since: 5.2.0
1263  */
1264 gboolean
gda_ldap_modify_entry(GdaLdapConnection * cnc,GdaLdapModificationType modtype,GdaLdapEntry * entry,GdaLdapEntry * ref_entry,GError ** error)1265 gda_ldap_modify_entry (GdaLdapConnection *cnc, GdaLdapModificationType modtype,
1266 		       GdaLdapEntry *entry, GdaLdapEntry *ref_entry, GError **error)
1267 {
1268 	g_return_val_if_fail (GDA_IS_LDAP_CONNECTION (cnc), FALSE);
1269 	g_return_val_if_fail (entry, FALSE);
1270 	g_return_val_if_fail (entry->dn && *(entry->dn), FALSE);
1271 
1272 	return _gda_ldap_modify (cnc, modtype, entry, ref_entry, error);
1273 }
1274