Browse code

- initial version of generic LDAP driver (not yet ready to be used)

Jan Janak authored on 12/05/2008 12:14:38
Showing 18 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,12 @@
1
+# $Id$
2
+#
3
+# WARNING: do not run this directly, it should be run by the master Makefile
4
+
5
+include ../../Makefile.defs
6
+auto_gen=
7
+NAME=ldap.so
8
+
9
+#DEFS += -DLD_TEST
10
+LIBS=-L$(LOCALBASE)/lib -L /usr/lib -lldap
11
+
12
+include ../../Makefile.modules
0 13
new file mode 100644
... ...
@@ -0,0 +1,406 @@
1
+/* 
2
+ * $Id$ 
3
+ *
4
+ * LDAP Database Driver for SER
5
+ *
6
+ * Copyright (C) 2008 iptelorg GmbH
7
+ *
8
+ * This file is part of SER, a free SIP server.
9
+ *
10
+ * SER is free software; you can redistribute it and/or modify it under the
11
+ * terms of the GNU General Public License as published by the Free Software
12
+ * Foundation; either version 2 of the License, or (at your option) any later
13
+ * version
14
+ *
15
+ * For a license to use the ser software under conditions other than those
16
+ * described here, or to purchase support for this software, please contact
17
+ * iptel.org by e-mail at the following addresses: info@iptel.org
18
+ *
19
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
20
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
+ * details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License along
25
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
26
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+
29
+/** \addtogroup ldap
30
+ * @{ 
31
+ */
32
+
33
+/** \file
34
+ * Implementation of functions related to database commands.
35
+ */
36
+
37
+#include "ld_cmd.h"
38
+#include "ld_fld.h"
39
+#include "ld_con.h"
40
+#include "ld_mod.h"
41
+#include "ld_uri.h"
42
+#include "ld_config.h"
43
+#include "ld_res.h"
44
+
45
+#include "../../mem/mem.h"
46
+#include "../../dprint.h"
47
+#include "../../ut.h"
48
+
49
+#include <string.h>
50
+
51
+
52
+/**
53
+ * Reallocatable string buffer.
54
+ */
55
+struct string_buffer {
56
+	char *s;			/**< allocated memory itself */
57
+	int   len;			/**< used memory */
58
+	int   size;			/**< total size of allocated memory */
59
+	int   increment;	/**< increment when realloc is necessary */ 
60
+};
61
+
62
+
63
+/** Appends string to string buffer.
64
+ * This function appends string to dynamically created string buffer,
65
+ * the buffer is automatically extended if there is not enough room
66
+ * in the buffer. The buffer is allocated using pkg_malloc.
67
+ * @param sb    string buffer
68
+ * @param nstr  string to add
69
+ * @return      0 if OK, -1 if failed
70
+ */
71
+static inline int sb_add(struct string_buffer *sb, str *nstr)
72
+{
73
+	int new_size = 0;
74
+	int rsize = sb->len + nstr->len;
75
+	int asize;
76
+	char *newp;
77
+	
78
+	if (rsize > sb->size) {
79
+		asize = rsize - sb->size;
80
+		new_size = sb->size + (asize / sb->increment  + 
81
+							   (asize % sb->increment > 0)) * sb->increment;
82
+		newp = pkg_malloc(new_size);
83
+		if (!newp) {
84
+			ERR("ldap: No memory left\n");
85
+			return -1;
86
+		}
87
+		if (sb->s) {
88
+			memcpy(newp, sb->s, sb->len);
89
+			pkg_free(sb->s);
90
+		}
91
+		sb->s = newp;
92
+		sb->size = new_size;
93
+	}
94
+	memcpy(sb->s + sb->len, nstr->s, nstr->len);
95
+	sb->len += nstr->len;
96
+	return 0;
97
+}
98
+
99
+
100
+/** Creates str string from zero terminated string without copying.
101
+ * This function initializes members of a temporary str structure
102
+ * with the pointer and lenght of the string from s parameter.
103
+ *
104
+ * @param str A pointer to temporary str structure.
105
+ * @param s   A zero terminated string.
106
+ * @return Pointer to the str structure.
107
+ */
108
+static inline str* set_str(str *str, const char *s)
109
+{
110
+	str->s = (char *)s;
111
+	str->len = strlen(s);
112
+	return str;
113
+}
114
+
115
+
116
+/** Destroys a ld_cmd structure.
117
+ * This function frees all memory used by ld_cmd structure.
118
+ * @param cmd A pointer to generic db_cmd command being freed.
119
+ * @param payload A pointer to ld_cmd structure to be freed.
120
+ */
121
+static void ld_cmd_free(db_cmd_t* cmd, struct ld_cmd* payload)
122
+{
123
+	db_drv_free(&payload->gen);
124
+	if (payload->result) pkg_free(payload->result);
125
+	pkg_free(payload);
126
+}
127
+
128
+
129
+static int build_result_array(char*** res, db_cmd_t* cmd)
130
+{
131
+	struct ld_fld* lfld;
132
+	char** t;
133
+	int i;
134
+	if (cmd->result_count == 0) {
135
+		*res = NULL;
136
+		return 0;
137
+	}
138
+	
139
+	t = (char**)pkg_malloc(sizeof(char*) * (cmd->result_count + 1));
140
+	if (t == NULL) {
141
+		ERR("ldap: No memory left\n");
142
+		return -1;
143
+	}
144
+	t[cmd->result_count] = NULL;
145
+
146
+	for(i = 0; i < cmd->result_count; i++) {
147
+		lfld = DB_GET_PAYLOAD(cmd->result + i);
148
+		/* Attribute names are always zero terminated */
149
+		t[i] = lfld->attr.s;
150
+	}
151
+
152
+	*res = t;
153
+	return 0;
154
+}
155
+
156
+
157
+static int build_search_filter(char** dst, db_fld_t* fld, str* filter_add)
158
+{
159
+	struct ld_fld* lfld;
160
+	struct string_buffer sql_buf = {.s = NULL, .len = 0, 
161
+									.size = 0, .increment = 128};
162
+	int i, rv = 0;
163
+	str tmpstr;
164
+	static str zt = STR_STATIC_INIT("\0");
165
+
166
+	/* Return NULL if there are no fields and no preconfigured search
167
+	 * string supplied in the configuration file
168
+	 */
169
+	if ((DB_FLD_EMPTY(fld) || DB_FLD_LAST(fld[0])) &&
170
+		((filter_add->s == NULL) || !filter_add->len)) {
171
+		*dst = NULL;
172
+		return 0;
173
+	}
174
+
175
+	rv = sb_add(&sql_buf, set_str(&tmpstr, "(&"));
176
+	if (filter_add->s && filter_add->len) {
177
+		/* Add the filter component specified in the config file */
178
+		rv |= sb_add(&sql_buf, filter_add);
179
+	}
180
+
181
+	for(i = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[i]); i++) {
182
+
183
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, "("));
184
+
185
+		lfld = DB_GET_PAYLOAD(fld + i);	
186
+		rv |= sb_add(&sql_buf, &lfld->attr);
187
+		switch(fld[i].op) {
188
+		case DB_EQ: /* The value of the field must be equal */
189
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, "="));
190
+			break;
191
+
192
+		case DB_LT:     /* The value of the field must be less than */
193
+		case DB_LEQ:    /* The value of the field must be less than or equal */
194
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, "<="));
195
+			break;
196
+
197
+		case DB_GT:     /* The value of the field must be greater than */
198
+		case DB_GEQ:     /* The value of the field must be greater than or equal */
199
+			rv |= sb_add(&sql_buf, set_str(&tmpstr, ">="));
200
+			break;
201
+
202
+		default:
203
+			ERR("ldap: Unsupported operator encountered: %d\n", fld[i].op);
204
+			goto error;
205
+		}
206
+
207
+		if ((fld[i].flags & DB_NULL) == 0) {
208
+			switch(fld[i].type) {
209
+			case DB_CSTR:
210
+				rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].v.cstr));
211
+				break;
212
+				
213
+			case DB_STR:
214
+				rv |= sb_add(&sql_buf, &fld[i].v.lstr);
215
+				break;
216
+			
217
+			default:
218
+				ERR("ldap: Unsupported field type encountered: %d\n", fld[i].type);
219
+				goto error;
220
+			}
221
+		}
222
+		
223
+		rv |= sb_add(&sql_buf, set_str(&tmpstr, ")"));
224
+	}
225
+
226
+	rv |= sb_add(&sql_buf, set_str(&tmpstr, ")"));
227
+	rv |= sb_add(&sql_buf, &zt);
228
+	if (rv) goto error;
229
+
230
+	*dst = sql_buf.s;
231
+	return 0;
232
+
233
+ error:
234
+	if (sql_buf.s) pkg_free(sql_buf.s);
235
+	return -1;	
236
+}
237
+
238
+
239
+int ld_cmd(db_cmd_t* cmd)
240
+{
241
+	struct ld_cmd* lcmd;
242
+	struct ld_config* cfg;
243
+ 
244
+	lcmd = (struct ld_cmd*)pkg_malloc(sizeof(struct ld_cmd));
245
+	if (lcmd == NULL) {
246
+		ERR("ldap: No memory left\n");
247
+		goto error;
248
+	}
249
+	memset(lcmd, '\0', sizeof(struct ld_cmd));
250
+	if (db_drv_init(&lcmd->gen, ld_cmd_free) < 0) goto error;
251
+
252
+	switch(cmd->type) {
253
+	case DB_PUT:
254
+		ERR("ldap: DB_PUT not supported\n");
255
+		goto error;
256
+		break;
257
+		
258
+	case DB_DEL:
259
+		ERR("ldap: DB_DEL not supported\n");
260
+		goto error;
261
+		break;
262
+
263
+	case DB_GET:		
264
+		break;
265
+
266
+	case DB_UPD:
267
+		ERR("ldap: DB_UPD not supported\n");
268
+		goto error;
269
+		break;
270
+		
271
+	case DB_SQL:
272
+		ERR("ldap: DB_SQL not supported\n");
273
+		goto error;
274
+        break;
275
+	}
276
+
277
+	cfg = ld_find_config(&cmd->table);
278
+	if (cfg == NULL) {
279
+		ERR("ldap: Cannot find configuration for '%.*s', giving up\n",
280
+			STR_FMT(&cmd->table));
281
+		goto error;
282
+	}
283
+
284
+	lcmd->base = cfg->base;
285
+	lcmd->scope = cfg->scope;
286
+
287
+	if (cfg->filter) {
288
+		lcmd->filter.s = cfg->filter;
289
+		lcmd->filter.len = strlen(cfg->filter);
290
+	}
291
+
292
+	if (ld_resolve_fld(cmd->match, cfg) < 0) goto error;
293
+	if (ld_resolve_fld(cmd->result, cfg) < 0) goto error;
294
+
295
+	if (build_result_array(&lcmd->result, cmd) < 0) goto error;
296
+
297
+	DB_SET_PAYLOAD(cmd, lcmd);
298
+	return 0;
299
+
300
+ error:
301
+	if (lcmd) {
302
+		DB_SET_PAYLOAD(cmd, NULL);
303
+		db_drv_free(&lcmd->gen);
304
+		if (lcmd->result) pkg_free(lcmd->result);
305
+		pkg_free(lcmd);
306
+	}
307
+	return -1;
308
+}
309
+
310
+
311
+int ld_cmd_exec(db_res_t* res, db_cmd_t* cmd)
312
+{
313
+	db_con_t* con;
314
+	struct ld_res* lres;
315
+	struct ld_cmd* lcmd;
316
+	struct ld_con* lcon;
317
+	char* filter;
318
+	int ret;
319
+	LDAPMessage* msg;
320
+
321
+	/* First things first: retrieve connection info from the currently active
322
+	 * connection and also mysql payload from the database command
323
+	 */
324
+	con = cmd->ctx->con[db_payload_idx];
325
+	lcmd = DB_GET_PAYLOAD(cmd);
326
+	lcon = DB_GET_PAYLOAD(con);
327
+
328
+	if (build_search_filter(&filter, cmd->match, &lcmd->filter) < 0) {
329
+		ERR("ldap: Error while building LDAP search filter\n");
330
+		return -1;
331
+	}
332
+
333
+	ret = ldap_search_ext_s(lcon->con, lcmd->base, lcmd->scope, filter,
334
+							lcmd->result, 0, NULL, NULL, NULL, 0, &msg);
335
+	if (filter) pkg_free(filter);
336
+
337
+	if (ret != LDAP_SUCCESS) {
338
+		ERR("ldap: Error in ldap_search: %s\n", ldap_err2string(ret));
339
+		return -1;
340
+	}
341
+
342
+	if (res) {
343
+		lres = DB_GET_PAYLOAD(res);
344
+		if (lres->msg) ldap_msgfree(lres->msg);
345
+		lres->msg = msg;
346
+	} else {
347
+		ldap_msgfree(msg);
348
+	}
349
+
350
+	return 0;
351
+}
352
+
353
+
354
+int ld_cmd_first(db_res_t* res)
355
+{
356
+	db_con_t* con;
357
+	struct ld_res* lres;
358
+	struct ld_con* lcon;
359
+
360
+	lres = DB_GET_PAYLOAD(res);
361
+	/* FIXME */
362
+	con = res->cmd->ctx->con[db_payload_idx];
363
+	lcon = DB_GET_PAYLOAD(con);
364
+
365
+	lres->current = ldap_first_message(lcon->con, lres->msg);
366
+	while(lres->current) {
367
+		if (ldap_msgtype(lres->current) == LDAP_RES_SEARCH_ENTRY) {
368
+			break;
369
+		}
370
+		lres->current = ldap_next_message(lcon->con, lres->msg);
371
+	}
372
+	if (lres->current == NULL) return 1;
373
+
374
+	if (ld_ldap2fld(res->cmd->result, lcon->con, lres->current) < 0) return -1;
375
+	res->cur_rec->fld = res->cmd->result;
376
+	return 0;
377
+}
378
+
379
+
380
+int ld_cmd_next(db_res_t* res)
381
+{
382
+	db_con_t* con;
383
+	struct ld_res* lres;
384
+	struct ld_con* lcon;
385
+
386
+	lres = DB_GET_PAYLOAD(res);
387
+	/* FIXME */
388
+	con = res->cmd->ctx->con[db_payload_idx];
389
+	lcon = DB_GET_PAYLOAD(con);
390
+
391
+	if (lres->current == NULL) return 1;
392
+
393
+	lres->current = ldap_next_message(lcon->con, lres->current);
394
+	while(lres->current) {
395
+		if (ldap_msgtype(lres->current) == LDAP_RES_SEARCH_ENTRY) {
396
+			break;
397
+		}
398
+		lres->current = ldap_next_message(lcon->con, lres->current);
399
+	}
400
+	if (lres->current == NULL) return 1;
401
+	if (ld_ldap2fld(res->cmd->result, lcon->con, lres->current) < 0) return -1;
402
+	res->cur_rec->fld = res->cmd->result;
403
+	return 0;
404
+}
405
+
406
+/** @} */
0 407
new file mode 100644
... ...
@@ -0,0 +1,92 @@
1
+/*
2
+ * $Id$ 
3
+ *
4
+ * LDAP Database Driver for SER
5
+ *
6
+ * Copyright (C) 2008 iptelorg GmbH
7
+ *
8
+ * This file is part of SER, a free SIP server.
9
+ *
10
+ * SER is free software; you can redistribute it and/or modify it under the
11
+ * terms of the GNU General Public License as published by the Free Software
12
+ * Foundation; either version 2 of the License, or (at your option) any later
13
+ * version
14
+ *
15
+ * For a license to use the ser software under conditions other than those
16
+ * described here, or to purchase support for this software, please contact
17
+ * iptel.org by e-mail at the following addresses: info@iptel.org
18
+ *
19
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
20
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
+ * details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License along
25
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
26
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+
29
+#ifndef _LD_CMD_H
30
+#define _LD_CMD_H
31
+
32
+/** \addtogroup ldap
33
+ * @{ 
34
+ */
35
+
36
+/** \file 
37
+ * Declaration of ld_cmd data structure that contains LDAP specific data
38
+ * stored in db_cmd structures and related functions.
39
+ */
40
+
41
+#include "../../db/db_drv.h"
42
+#include "../../db/db_cmd.h"
43
+#include "../../db/db_res.h"
44
+#include "../../str.h"
45
+
46
+#include <stdarg.h>
47
+
48
+
49
+/** Extension structure of db_cmd adding LDAP specific data.
50
+ * This data structure extends the generic data structure db_cmd in the
51
+ * database API with data specific to the ldap driver.
52
+ */
53
+struct ld_cmd {
54
+	db_drv_t gen; /**< Generic part of the data structure (must be first */
55
+	char* base;   /**< Search base of the command */
56
+	int scope;    /**< Scope of the search */
57
+	str filter;   /**< To be added to the search filter */
58
+	char** result; /**< An array with result attribute names for ldap_search */
59
+};
60
+
61
+
62
+/** Creates a new ld_cmd data structure.
63
+ * This function allocates and initializes memory for a new ld_cmd data
64
+ * structure. The data structure is then attached to the generic db_cmd
65
+ * structure in cmd parameter.
66
+ * @param cmd A generic db_cmd structure to which the newly created ld_cmd
67
+ *            structure will be attached.
68
+ */
69
+int ld_cmd(db_cmd_t* cmd);
70
+
71
+
72
+/** The main execution function in ldap SER driver.
73
+ * This is the main execution function in this driver. It is executed whenever
74
+ * a SER module calls db_exec and the target database of the commands is
75
+ * ldap.
76
+ * @param res A pointer to (optional) result structure if the command returns
77
+ *            a result.
78
+ * @retval 0 if executed successfully
79
+ * @retval A negative number if the database server failed to execute command
80
+ * @retval A positive number if there was an error on client side (SER)
81
+ */
82
+int ld_cmd_exec(db_res_t* res, db_cmd_t* cmd);
83
+
84
+
85
+int ld_cmd_first(db_res_t* res);
86
+
87
+
88
+int ld_cmd_next(db_res_t* res);
89
+
90
+/** @} */
91
+
92
+#endif /* _LD_CMD_H */
0 93
new file mode 100644
... ...
@@ -0,0 +1,218 @@
1
+/* 
2
+ * $Id$ 
3
+ *
4
+ * LDAP Database Driver for SER
5
+ *
6
+ * Copyright (C) 2008 iptelorg GmbH
7
+ *
8
+ * This file is part of SER, a free SIP server.
9
+ *
10
+ * SER is free software; you can redistribute it and/or modify it under the
11
+ * terms of the GNU General Public License as published by the Free Software
12
+ * Foundation; either version 2 of the License, or (at your option) any later
13
+ * version
14
+ *
15
+ * For a license to use the ser software under conditions other than those
16
+ * described here, or to purchase support for this software, please contact
17
+ * iptel.org by e-mail at the following addresses: info@iptel.org
18
+ *
19
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
20
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
+ * details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License along
25
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
26
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+
29
+/** \addtogroup ldap
30
+ * @{ 
31
+ */
32
+
33
+/** \file 
34
+ * Functions related to connections to LDAP servers.
35
+ */
36
+
37
+#define LDAP_DEPRECATED 1
38
+
39
+#include "ld_con.h"
40
+#include "ld_uri.h"
41
+
42
+#include "../../mem/mem.h"
43
+#include "../../dprint.h"
44
+#include "../../ut.h"
45
+
46
+#include <ldap.h>
47
+#include <malloc.h>
48
+#include <string.h>
49
+
50
+
51
+/** Free all memory allocated for a ld_con structure.
52
+ * This function function frees all memory that is in use by
53
+ * a ld_con structure.
54
+ * @param con A generic db_con connection structure.
55
+ * @param payload LDAP specific payload to be freed.
56
+ */
57
+static void ld_con_free(db_con_t* con, struct ld_con* payload)
58
+{
59
+	struct ld_uri* luri;
60
+	int ret;
61
+	if (!payload) return;
62
+
63
+	luri = DB_GET_PAYLOAD(con->uri);
64
+
65
+	/* Delete the structure only if there are no more references
66
+	 * to it in the connection pool
67
+	 */
68
+	if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
69
+	
70
+	db_pool_entry_free(&payload->gen);
71
+	if (payload->con) {
72
+		ret = ldap_unbind_ext_s(payload->con, NULL, NULL);
73
+		if (ret != LDAP_SUCCESS) {
74
+			ERR("ldap: Error while unbinding from %s: %s\n", 
75
+				luri->uri, ldap_err2string(ret));
76
+		}
77
+	}
78
+	pkg_free(payload);
79
+}
80
+
81
+
82
+int ld_con(db_con_t* con)
83
+{
84
+	struct ld_con* lcon;
85
+	struct ld_uri* luri;
86
+
87
+	luri = DB_GET_PAYLOAD(con->uri);
88
+
89
+	/* First try to lookup the connection in the connection pool and
90
+	 * re-use it if a match is found
91
+	 */
92
+	lcon = (struct ld_con*)db_pool_get(con->uri);
93
+	if (lcon) {
94
+		DBG("ldap: Connection to %s found in connection pool\n",
95
+			luri->uri);
96
+		goto found;
97
+	}
98
+
99
+	lcon = (struct ld_con*)pkg_malloc(sizeof(struct ld_con));
100
+	if (!lcon) {
101
+		ERR("ldap: No memory left\n");
102
+		goto error;
103
+	}
104
+	memset(lcon, '\0', sizeof(struct ld_con));
105
+	if (db_pool_entry_init(&lcon->gen, ld_con_free, con->uri) < 0) goto error;
106
+
107
+	DBG("ldap: Preparing new connection to %s\n", luri->uri);
108
+
109
+	/* Put the newly created postgres connection into the pool */
110
+	db_pool_put((struct db_pool_entry*)lcon);
111
+	DBG("ldap: Connection stored in connection pool\n");
112
+
113
+ found:
114
+	/* Attach driver payload to the db_con structure and set connect and
115
+	 * disconnect functions
116
+	 */
117
+	DB_SET_PAYLOAD(con, lcon);
118
+	con->connect = ld_con_connect;
119
+	con->disconnect = ld_con_disconnect;
120
+	return 0;
121
+
122
+ error:
123
+	if (lcon) {
124
+		db_pool_entry_free(&lcon->gen);
125
+		pkg_free(lcon);
126
+	}
127
+	return -1;
128
+}
129
+
130
+
131
+int ld_con_connect(db_con_t* con)
132
+{
133
+	struct ld_con* lcon;
134
+	struct ld_uri* luri;
135
+	int ret, version = 3;
136
+	
137
+	lcon = DB_GET_PAYLOAD(con);
138
+	luri = DB_GET_PAYLOAD(con->uri);
139
+	
140
+	/* Do not reconnect already connected connections */
141
+	if (lcon->flags & LD_CONNECTED) return 0;
142
+
143
+	DBG("ldap: Connecting to %s\n", luri->uri);
144
+
145
+	if (lcon->con) {
146
+		ret = ldap_unbind_ext_s(lcon->con, NULL, NULL);
147
+		if (ret != LDAP_SUCCESS) {
148
+			ERR("ldap: Error while unbinding from %s: %s\n", 
149
+				luri->uri, ldap_err2string(ret));
150
+		}
151
+	}
152
+
153
+	ret = ldap_initialize(&lcon->con, luri->uri);
154
+	if (lcon->con == NULL) {
155
+		ERR("ldap: Error while initializing new LDAP connection to %s\n",
156
+			luri->uri);
157
+		goto error;
158
+	}
159
+
160
+	ret = ldap_set_option(lcon->con, LDAP_OPT_PROTOCOL_VERSION, &version);
161
+	if (ret != LDAP_OPT_SUCCESS) {
162
+		ERR("ldap: Error while setting protocol version 3: %s\n", 
163
+			ldap_err2string(ret));
164
+		goto error;
165
+	}
166
+
167
+	/* FIXME: Currently only anonymous binds are supported */
168
+	ret = ldap_simple_bind_s(lcon->con, NULL, NULL);
169
+	if (ret != LDAP_SUCCESS) {
170
+		ERR("ldap: Bind to %s failed: %s\n", 
171
+			luri->uri, ldap_err2string(ret));
172
+		goto error;
173
+	}
174
+
175
+	DBG("ldap: Successfully bound to %s\n", luri->uri);
176
+	lcon->flags |= LD_CONNECTED;
177
+	return 0;
178
+
179
+ error:
180
+	if (lcon->con) {
181
+		ret = ldap_unbind_ext_s(lcon->con, NULL, NULL);
182
+		if (ret) {
183
+			ERR("ldap: Error while unbinding from %s: %s\n", 
184
+				luri->uri, ldap_err2string(ret));
185
+		}
186
+	}
187
+	lcon->con = NULL;
188
+	return -1;
189
+}
190
+
191
+
192
+void ld_con_disconnect(db_con_t* con)
193
+{
194
+	struct ld_con* lcon;
195
+	struct ld_uri* luri;
196
+	int ret;
197
+
198
+	lcon = DB_GET_PAYLOAD(con);
199
+	luri = DB_GET_PAYLOAD(con->uri);
200
+
201
+	if ((lcon->flags & LD_CONNECTED) == 0) return;
202
+
203
+	DBG("ldap: Unbinding from %s\n", luri->uri);
204
+
205
+	if (lcon->con) {
206
+		ret = ldap_unbind_ext_s(lcon->con, NULL, NULL);
207
+		if (ret) {
208
+			ERR("ldap: Error while unbinding from %s: %s\n", 
209
+				luri->uri, ldap_err2string(ret));
210
+		}
211
+	}
212
+
213
+	lcon->con = NULL;
214
+	lcon->flags &= ~LD_CONNECTED;
215
+}
216
+
217
+
218
+/** @} */
0 219
new file mode 100644
... ...
@@ -0,0 +1,96 @@
1
+/* 
2
+ * $Id$ 
3
+ *
4
+ * LDAP Database Driver for SER
5
+ *
6
+ * Copyright (C) 2008 iptelorg GmbH
7
+ *
8
+ * This file is part of SER, a free SIP server.
9
+ *
10
+ * SER is free software; you can redistribute it and/or modify it under the
11
+ * terms of the GNU General Public License as published by the Free Software
12
+ * Foundation; either version 2 of the License, or (at your option) any later
13
+ * version
14
+ *
15
+ * For a license to use the ser software under conditions other than those
16
+ * described here, or to purchase support for this software, please contact
17
+ * iptel.org by e-mail at the following addresses: info@iptel.org
18
+ *
19
+ * SER is distributed in the hope that it will be useful, but WITHOUT ANY
20
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
+ * details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License along
25
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
26
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+
29
+#ifndef _LD_CON_H
30
+#define _LD_CON_H
31
+
32
+/** \addtogroup ldap
33
+ * @{ 
34
+ */
35
+
36
+/** \file 
37
+ * Implementation of LDAP per-connection related data structures and functions.
38
+ */
39
+
40
+#include "../../db/db_pool.h"
41
+#include "../../db/db_con.h"
42
+#include "../../db/db_uri.h"
43
+
44
+#include <time.h>
45
+#include <ldap.h>
46
+
47
+/** 
48
+ * Per-connection flags for LDAP connections.
49
+ */
50
+enum ld_con_flags {
51
+	LD_CONNECTED      = (1 << 0), /**< The connection has been connected successfully */
52
+};
53
+
54
+
55
+/** A structure representing a connection to a LDAP server.
56
+ * This structure represents connections to LDAP servers. It contains
57
+ * LDAP specific per-connection data, 
58
+ */
59
+struct ld_con {
60
+	db_pool_entry_t gen;  /**< Generic part of the structure */
61
+	LDAP* con;            /**< LDAP connection handle */
62
+	unsigned int flags;   /**< Flags */
63
+};
64
+
65
+
66
+/** Create a new ld_con structure.
67
+ * This function creates a new ld_con structure and attachs the structure to
68
+ * the generic db_con structure in the parameter.
69
+ * @param con A generic db_con structure to be extended with LDAP payload
70
+ * @retval 0 on success
71
+ * @retval A negative number on error
72
+ */
73
+int ld_con(db_con_t* con);
74
+
75
+
76
+/** Establish a new connection to server.  
77
+ * This function is called when a SER module calls db_connect to establish a
78
+ * new connection to the database server. After the connection is established
79
+ * the function sends an SQL query to the server to determine the format of
80
+ * timestamp fields and also obtains the list of supported field types.
81
+ * @param con A structure representing database connection.
82
+ * @retval 0 on success.
83
+ * @retval A negative number on error.
84
+ */
85
+int ld_con_connect(db_con_t* con);
86
+
87
+
88
+/** Disconnected from LDAP server.
89
+ * Disconnects a previously connected connection to LDAP server.
90
+ * @param con A structure representing the connection to be disconnected.
91
+ */
92
+void ld_con_disconnect(db_con_t* con);
93
+
94
+/** @} */
95
+
96
+#endif /* _LD_CON_H */
0 97
new file mode 100644
... ...
@@ -0,0 +1,876 @@
1
+/*
2
+ * LDAP module - Configuration file parser
3
+ *
4
+ * Copyright (C) 2001-2003 FhG FOKUS
5
+ * Copyright (C) 2004,2005 Free Software Foundation, Inc.
6
+ * Copyright (C) 2005,2006 iptelorg GmbH
7
+ *
8
+ * This file is part of ser, a free SIP server.
9
+ *
10
+ * ser is free software; you can redistribute it and/or modify
11
+ * it under the terms of the GNU General Public License as published by
12
+ * the Free Software Foundation; either version 2 of the License, or
13
+ * (at your option) any later version
14
+ *
15
+ * For a license to use the ser software under conditions
16
+ * other than those described here, or to purchase support for this
17
+ * software, please contact iptel.org by e-mail at the following addresses:
18
+ *    info@iptel.org
19
+ *
20
+ * ser is distributed in the hope that it will be useful,
21
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
+ * GNU General Public License for more details.
24
+ *
25
+ * You should have received a copy of the GNU General Public License
26
+ * along with this program; if not, write to the Free Software
27
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
+ *
29
+ */
30
+
31
+#include "ld_config.h"
32
+#include "ld_mod.h"
33
+
34
+#include "../../mem/mem.h"
35
+#include "../../dprint.h"
36
+#include "../../trim.h"
37
+#include "../../ut.h"
38
+#include "../../resolve.h"
39
+
40
+#include <ldap.h>
41
+#include <malloc.h>
42
+#include <stdio.h>
43
+#include <libgen.h>
44
+
45
+#define MAX_TOKEN_LEN 256
46
+
47
+
48
+struct ld_config* ld_cfg_root = NULL;
49
+
50
+
51
+/*
52
+ * Parser state
53
+ */
54
+static struct {
55
+	FILE* f;
56
+	char* file;
57
+	int line;
58
+	int col;
59
+	struct ld_config* cfg; /* Current configuration data */
60
+} pstate;
61
+
62
+
63
+/*
64
+ * Structure representing lexical token
65
+ */
66
+typedef struct token {
67
+	char buf [MAX_TOKEN_LEN];
68
+	int type;  /* Token type */
69
+	str val;   /* Token value */
70
+
71
+	struct {   /* Position of first and last character of
72
+		    * token in file
73
+		    */
74
+		int line;
75
+		int col;
76
+	} start, end;
77
+} token_t;
78
+
79
+
80
+typedef int (*parser_func_f)(token_t* token);
81
+
82
+
83
+struct parser_tab {
84
+	str token;
85
+	union {
86
+		int ival;
87
+		str sval;
88
+		parser_func_f fval;
89
+	} u;
90
+};
91
+
92
+static struct parser_tab token_scope[];
93
+static struct parser_tab option_name[];
94
+
95
+
96
+/*
97
+ * The states of the lexical scanner
98
+ */
99
+enum st {
100
+	ST_S,  /* Begin */
101
+	ST_A,  /* Alphanumeric */
102
+	ST_AE, /* Alphanumeric escaped */
103
+	ST_Q,  /* Quoted */
104
+	ST_QE, /* Quoted escaped */
105
+	ST_C,  /* Comment */
106
+	ST_CE, /* Comment escaped */
107
+	ST_E,  /* Escaped */
108
+};
109
+
110
+
111
+/* Extended tokens can contain also delimiters,
112
+ * in addition to alpha-numeric characters,
113
+ * this is used on the righ side of assignments
114
+ * where no quotes are used
115
+ */
116
+#define EXTENDED_ALPHA_TOKEN (1 << 0)
117
+
118
+
119
+/*
120
+ * Test for alphanumeric characters
121
+ */
122
+#define IS_ALPHA(c) \
123
+    (((c) >= 'a' && (c) <= 'z') || \
124
+     ((c) >= 'A' && (c) <= 'Z') || \
125
+     ((c) >= '0' && (c) <= '9') || \
126
+     (c) == '_')
127
+
128
+
129
+/*
130
+ * Test for delimiter characters
131
+ */
132
+#define IS_DELIM(c) \
133
+    ((c) == '=' || \
134
+     (c) == ':' || \
135
+     (c) == ';' || \
136
+     (c) == '.' || \
137
+     (c) == ',' || \
138
+     (c) == '?' || \
139
+     (c) == '[' || \
140
+     (c) == ']' || \
141
+     (c) == '/' || \
142
+     (c) == '@' || \
143
+     (c) == '!' || \
144
+     (c) == '$' || \
145
+     (c) == '%' || \
146
+     (c) == '&' || \
147
+     (c) == '*' || \
148
+     (c) == '(' || \
149
+     (c) == ')' || \
150
+     (c) == '-' || \
151
+     (c) == '+' || \
152
+     (c) == '|' || \
153
+     (c) == '\'')
154
+
155
+/* Whitespace characters */
156
+#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r') 
157
+#define IS_QUOTE(c)      ((c) == '\"')  /* Quote characters */
158
+#define IS_COMMENT(c)    ((c) == '#')   /* Characters that start comments */
159
+#define IS_ESCAPE(c)     ((c) == '\\')  /* Escape characters */
160
+#define IS_EOL(c)        ((c) == '\n')  /* End of line */
161
+
162
+
163
+/*
164
+ * Append character to the value of current
165
+ * token
166
+ */
167
+#define PUSH(c)                                    \
168
+    if (token->val.len >= MAX_TOKEN_LEN) {         \
169
+        ERR("%s:%d:%d: Token too long\n",          \
170
+        pstate.file, pstate.line, pstate.col);     \
171
+        return -1;                                 \
172
+    }                                              \
173
+    if (token->val.len == 0) {                     \
174
+         token->start.line = pstate.line;          \
175
+         token->start.col = pstate.col;            \
176
+    }                                              \
177
+    token->val.s[token->val.len++] = (c);
178
+
179
+
180
+/*
181
+ * Return current token from the lexical analyzer
182
+ */
183
+#define RETURN(c)                  \
184
+    token->end.line = pstate.line; \
185
+    token->end.col = pstate.col;   \
186
+    token->type = (c);             \
187
+    print_token(token);            \
188
+    return 1;
189
+
190
+
191
+/*
192
+ * Get next character and update counters
193
+ */
194
+#define READ_CHAR         \
195
+     c = fgetc(pstate.f); \
196
+     if (IS_EOL(c)) {     \
197
+         pstate.line++;   \
198
+         pstate.col = 0;  \
199
+     } else {             \
200
+         pstate.col++;    \
201
+     }
202
+
203
+
204
+enum {
205
+	TOKEN_EOF = -1,
206
+	TOKEN_ALPHA = -2,
207
+	TOKEN_STRING = -3
208
+};
209
+
210
+
211
+static void print_token(struct token* token)
212
+{
213
+	DBG("token(%d, '%.*s', <%d,%d>-<%d,%d>)\n", 
214
+	    token->type, token->val.len, ZSW(token->val.s),
215
+	    token->start.line, token->start.col, 
216
+	    token->end.line, token->end.col);
217
+}
218
+
219
+
220
+static int lex(token_t* token, unsigned int flags)
221
+{
222
+	static int look_ahead = EOF;
223
+	int c;
224
+	enum st state;
225
+
226
+	state = ST_S;
227
+	
228
+	token->val.s = token->buf;
229
+	token->val.len = 0;
230
+
231
+	if (look_ahead != EOF) {
232
+		c = look_ahead;
233
+		look_ahead = EOF;
234
+	} else {
235
+		READ_CHAR;
236
+	}
237
+
238
+	while(c != EOF) {
239
+		switch(state) {
240
+		case ST_S:
241
+			if (flags & EXTENDED_ALPHA_TOKEN) {
242
+				if (IS_WHITESPACE(c)) {
243
+					     /* Do nothing */
244
+				} else if (IS_ALPHA(c) ||
245
+					   IS_ESCAPE(c) ||
246
+					   IS_DELIM(c)) {
247
+					PUSH(c);
248
+					state = ST_A;
249
+				} else if (IS_QUOTE(c)) {
250
+					state = ST_Q;
251
+				} else if (IS_COMMENT(c)) {
252
+					state = ST_C;
253
+				} else if (IS_EOL(c)) {
254
+					PUSH(c);
255
+					RETURN(c);
256
+				} else {
257
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
258
+					    pstate.file, pstate.line, pstate.col, c);
259
+					return -1;
260
+				}
261
+			} else {
262
+				if (IS_WHITESPACE(c)) {
263
+					     /* Do nothing */
264
+				} else if (IS_ALPHA(c)) {
265
+					PUSH(c);
266
+					state = ST_A;
267
+				} else if (IS_QUOTE(c)) {
268
+					state = ST_Q;
269
+				} else if (IS_COMMENT(c)) {
270
+					state = ST_C;
271
+				} else if (IS_ESCAPE(c)) {
272
+					state = ST_E;
273
+				} else if (IS_DELIM(c) || IS_EOL(c)) {
274
+					PUSH(c);
275
+					RETURN(c);
276
+				} else {
277
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
278
+					    pstate.file, pstate.line, pstate.col, c);
279
+					return -1;
280
+				}
281
+			}
282
+			break;
283
+
284
+		case ST_A:
285
+			if (flags & EXTENDED_ALPHA_TOKEN) {
286
+				if (IS_ALPHA(c) ||
287
+				    IS_DELIM(c) ||
288
+				    IS_QUOTE(c)) {
289
+					PUSH(c);
290
+				} else if (IS_ESCAPE(c)) {
291
+					state = ST_AE;
292
+				} else if (IS_COMMENT(c) || IS_EOL(c) || IS_WHITESPACE(c)) {
293
+					look_ahead = c;
294
+					RETURN(TOKEN_ALPHA);
295
+				} else {
296
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
297
+					    pstate.file, pstate.line, pstate.col, c);
298
+					return -1;
299
+				}
300
+			} else {
301
+				if (IS_ALPHA(c)) {
302
+					PUSH(c);
303
+				} else if (IS_ESCAPE(c)) {
304
+					state = ST_AE;
305
+				} else if (IS_WHITESPACE(c) ||
306
+					   IS_DELIM(c) ||
307
+					   IS_QUOTE(c) ||
308
+					   IS_COMMENT(c) ||
309
+					   IS_EOL(c)) {
310
+					look_ahead = c;
311
+					RETURN(TOKEN_ALPHA);
312
+				} else {
313
+					ERR("%s:%d:%d: Invalid character 0x%x\n", 
314
+					    pstate.file, pstate.line, pstate.col, c);
315
+					return -1;
316
+				}
317
+			}
318
+			break;
319
+
320
+		case ST_AE:
321
+			if (IS_COMMENT(c) ||
322
+			    IS_QUOTE(c) ||
323
+			    IS_ESCAPE(c)) {
324
+				PUSH(c);
325
+			} else if (c == 'r') {
326
+				PUSH('\r');
327
+			} else if (c == 'n') {
328
+				PUSH('\n');
329
+			} else if (c == 't') {
330
+				PUSH('\t');
331
+			} else if (c == ' ') {
332
+				PUSH(' ');
333
+			} else if (IS_EOL(c)) {
334
+				     /* Do nothing */
335
+			} else {
336
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
337
+				    pstate.file, pstate.line, pstate.col, c);
338
+				return -1;
339
+			}
340
+			state = ST_A;
341
+			break;
342
+
343
+		case ST_Q:
344
+			if (IS_QUOTE(c)) {
345
+				RETURN(TOKEN_STRING);
346
+			} else if (IS_ESCAPE(c)) {
347
+				state = ST_QE;
348
+				break;
349
+			} else {
350
+				PUSH(c);
351
+			}
352
+			break;
353
+
354
+		case ST_QE:
355
+			if (IS_ESCAPE(c) ||
356
+			    IS_QUOTE(c)) {
357
+				PUSH(c);
358
+			} else if (c == 'n') {
359
+				PUSH('\n');
360
+			} else if (c == 'r') {
361
+				PUSH('\r');
362
+			} else if (c == 't') {
363
+				PUSH('\t');
364
+			} else if (IS_EOL(c)) {
365
+				     /* Do nothing */
366
+			} else {
367
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
368
+				    pstate.file, pstate.line, pstate.col, c);
369
+				return -1;
370
+			}
371
+			state = ST_Q;
372
+			break;
373
+
374
+		case ST_C:
375
+			if (IS_ESCAPE(c)) {
376
+				state = ST_CE;
377
+			} else if (IS_EOL(c)) {
378
+				state = ST_S;
379
+				continue; /* Do not read a new char, return EOL */
380
+			} else {
381
+				     /* Do nothing */
382
+			}
383
+			break;
384
+
385
+		case ST_CE:
386
+			state = ST_C;
387
+			break;
388
+
389
+		case ST_E:
390
+			if (IS_COMMENT(c) ||
391
+			    IS_QUOTE(c) ||
392
+			    IS_ESCAPE(c)) {
393
+				PUSH(c);
394
+				RETURN(c);
395
+			} else if (c == 'r') {
396
+				PUSH('\r');
397
+				RETURN('\r');
398
+			} else if (c == 'n') {
399
+				PUSH('\n');
400
+				RETURN('\n');
401
+			} else if (c == 't') {
402
+				PUSH('\t');
403
+				RETURN('\t');
404
+			} else if (c == ' ') {
405
+				PUSH(' ');
406
+				RETURN(' ');
407
+			} else if (IS_EOL(c)) {
408
+				     /* Escped eol means no eol */
409
+				state = ST_S;
410
+			} else {
411
+				ERR("%s:%d:%d: Unsupported escape character 0x%x\n", 
412
+				    pstate.file, pstate.line, pstate.col, c);
413
+				return -1;
414
+			}
415
+			break;
416
+		}
417
+
418
+		READ_CHAR;
419
+	};
420
+
421
+	switch(state) {
422
+	case ST_S: 
423
+	case ST_C:
424
+	case ST_CE:
425
+		return 0;
426
+
427
+	case ST_A:
428
+		RETURN(TOKEN_ALPHA);
429
+
430
+	case ST_Q:
431
+		ERR("%s:%d:%d: Premature end of file, missing closing quote in"
432
+				" string constant\n", pstate.file, pstate.line, pstate.col);
433
+		return -1;
434
+
435
+	case ST_QE:
436
+	case ST_E:
437
+	case ST_AE:
438
+		ERR("%s:%d:%d: Premature end of file, missing escaped character\n", 
439
+		    pstate.file, pstate.line, pstate.col);
440
+		return -1;
441
+	}
442
+	BUG("%s:%d:%d: invalid state %d\n",
443
+			pstate.file, pstate.line, pstate.col, state);
444
+		return -1;
445
+}
446
+
447
+
448
+static struct parser_tab* lookup_token(struct parser_tab* table, str* token)
449
+{
450
+	struct parser_tab* ptr;
451
+
452
+	ptr = table;
453
+	while(ptr->token.s && ptr->token.len) {
454
+		if (token->len == ptr->token.len && 
455
+		    !strncasecmp(token->s, ptr->token.s, token->len)) {
456
+			return ptr;
457
+		}
458
+		ptr++;
459
+	}
460
+	return 0;
461
+}
462
+
463
+static int parse_string_val(str* res, token_t* token)
464
+{
465
+	int ret;
466
+	static token_t t;
467
+
468
+	ret = lex(&t, 0);
469
+	if (ret < 0) return -1;
470
+	if (ret == 0) {
471
+		ERR("ldap:%s:%d:%d: Option value missing\n", 
472
+		    pstate.file, token->start.line, token->start.col);
473
+		return -1;
474
+	}
475
+
476
+	if (t.type != '=') {
477
+		ERR("ldap:%s:%d:%d: Syntax error, '=' expected\n", 
478
+		    pstate.file, t.start.line, t.start.col);
479
+		return -1;
480
+	}
481
+
482
+	ret = lex(&t, EXTENDED_ALPHA_TOKEN);
483
+	if (ret < 0) return -1;
484
+	if (ret == 0) {
485
+		ERR("ldap:%s:%d:%d: Option value missing\n",
486
+		    pstate.file, t.start.line, t.start.col);
487
+		return -1;
488
+	}	
489
+
490
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
491
+		ERR("ldap:%s:%d:%d: Invalid option value '%.*s'\n", 
492
+		    pstate.file, t.start.line, t.start.col,
493
+		    t.val.len, ZSW(t.val.s));
494
+		return -1;
495
+	}
496
+
497
+	*res = t.val;;
498
+	return 0;
499
+}
500
+
501
+
502
+static int parse_search_scope(token_t* token)
503
+{
504
+	int ret;
505
+	token_t t;
506
+	struct parser_tab* r;
507
+
508
+	ret = lex(&t, 0);
509
+	if (ret < 0) return -1;
510
+	if (ret == 0) {
511
+		ERR("ldap:%s:%d:%d: Option value missing\n", 
512
+		    pstate.file, token->start.line, token->start.col);
513
+		return -1;
514
+	}
515
+
516
+	if (t.type != '=') {
517
+		ERR("ldap:%s:%d:%d: Syntax error, '=' expected\n", 
518
+		    pstate.file, t.start.line, t.start.col);
519
+		return -1;
520
+	}
521
+
522
+	ret = lex(&t, EXTENDED_ALPHA_TOKEN);
523
+	if (ret < 0) return -1;
524
+	if (ret == 0) {
525
+		ERR("ldap:%s:%d:%d: Option value missing\n",
526
+		    pstate.file, t.start.line, t.start.col);
527
+		return -1;
528
+	}	
529
+
530
+	if (t.type != TOKEN_ALPHA && t.type != TOKEN_STRING) {
531
+		ERR("ldap:%s:%d:%d: Invalid option value '%.*s'\n", 
532
+		    pstate.file, t.start.line, t.start.col,
533
+		    t.val.len, ZSW(t.val.s));
534
+		return -1;
535
+	}
536
+
537
+	r = lookup_token(token_scope, &t.val);
538
+	if (!r) {
539
+		ERR("ldap:%s:%d:%d: Invalid option value '%.*s'\n", 
540
+		    pstate.file, t.start.line, t.start.col,
541
+		    t.val.len, ZSW(t.val.s));
542
+		return -1;
543
+	}
544
+
545
+	pstate.cfg->scope = r->u.ival;
546
+	return 0;	
547
+}
548
+
549
+static int parse_search_base(token_t* token)
550
+{
551
+	str val;
552
+	if (parse_string_val(&val, token) < 0) return -1;
553
+	pstate.cfg->base = as_asciiz(&val);
554
+	if (pstate.cfg->base == NULL) {
555
+		ERR("ldap:%s:%d:%d: Out of memory while processing token %d:'%.*s'\n", 
556
+		    pstate.file, token->start.line, token->start.col,
557
+		    token->type, token->val.len, ZSW(token->val.s));
558
+		return -1;
559
+	}
560
+	return 0;
561
+}
562
+
563
+
564
+static int parse_search_filter(token_t* token)
565
+{
566
+	str val;
567
+	if (parse_string_val(&val, token) < 0) return -1;
568
+	pstate.cfg->filter = as_asciiz(&val);
569
+	if (pstate.cfg->filter == NULL) {
570
+		ERR("ldap:%s:%d:%d: Out of memory while processing token %d:'%.*s'\n", 
571
+		    pstate.file, token->start.line, token->start.col,
572
+		    token->type, token->val.len, ZSW(token->val.s));
573
+		return -1;
574
+	}
575
+	return 0;
576
+}
577
+
578
+
579
+static int parse_field_map(token_t* token)
580
+{
581
+	int ret;
582
+	token_t t;
583
+	void* ptr;
584