xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-sql/config.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /*	$NetBSD: config.c,v 1.1.1.4 2014/05/28 09:58:51 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1999-2014 The OpenLDAP Foundation.
7  * Portions Copyright 1999 Dmitry Kovalev.
8  * Portions Copyright 2002 Pierangelo Masarati.
9  * Portions Copyright 2004 Mark Adamson.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted only as authorized by the OpenLDAP
14  * Public License.
15  *
16  * A copy of this license is available in the file LICENSE in the
17  * top-level directory of the distribution or, alternatively, at
18  * <http://www.OpenLDAP.org/license.html>.
19  */
20 /* ACKNOWLEDGEMENTS:
21  * This work was initially developed by Dmitry Kovalev for inclusion
22  * by OpenLDAP Software.  Additional significant contributors include
23  * Pierangelo Masarati.
24  */
25 
26 #include "portable.h"
27 
28 #include <stdio.h>
29 #include "ac/string.h"
30 #include <sys/types.h>
31 
32 #include "slap.h"
33 #include "config.h"
34 #include "ldif.h"
35 #include "lutil.h"
36 #include "proto-sql.h"
37 
38 static int
39 create_baseObject(
40 	BackendDB	*be,
41 	const char	*fname,
42 	int		lineno );
43 
44 static int
45 read_baseObject(
46 	BackendDB	*be,
47 	const char	*fname );
48 
49 static ConfigDriver sql_cf_gen;
50 
51 enum {
52 	BSQL_CONCAT_PATT = 1,
53 	BSQL_CREATE_NEEDS_SEL,
54 	BSQL_UPPER_NEEDS_CAST,
55 	BSQL_HAS_LDAPINFO_DN_RU,
56 	BSQL_FAIL_IF_NO_MAPPING,
57 	BSQL_ALLOW_ORPHANS,
58 	BSQL_BASE_OBJECT,
59 	BSQL_LAYER,
60 	BSQL_SUBTREE_SHORTCUT,
61 	BSQL_FETCH_ALL_ATTRS,
62 	BSQL_FETCH_ATTRS,
63 	BSQL_CHECK_SCHEMA,
64 	BSQL_ALIASING_KEYWORD,
65 	BSQL_AUTOCOMMIT
66 };
67 
68 static ConfigTable sqlcfg[] = {
69 	{ "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
70 		(void *)offsetof(struct backsql_info, sql_dbhost),
71 		"( OLcfgDbAt:6.1 NAME 'olcDbHost' "
72 			"DESC 'Hostname of SQL server' "
73 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
74 	{ "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
75 		(void *)offsetof(struct backsql_info, sql_dbname),
76 		"( OLcfgDbAt:6.2 NAME 'olcDbName' "
77 			"DESC 'Name of SQL database' "
78 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
79 	{ "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
80 		(void *)offsetof(struct backsql_info, sql_dbuser),
81 		"( OLcfgDbAt:6.3 NAME 'olcDbUser' "
82 			"DESC 'Username for SQL session' "
83 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
84 	{ "dbpasswd", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
85 		(void *)offsetof(struct backsql_info, sql_dbpasswd),
86 		"( OLcfgDbAt:6.4 NAME 'olcDbPass' "
87 			"DESC 'Password for SQL session' "
88 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
89 	{ "concat_pattern", "pattern", 2, 2, 0,
90 		ARG_STRING|ARG_MAGIC|BSQL_CONCAT_PATT, (void *)sql_cf_gen,
91 		"( OLcfgDbAt:6.20 NAME 'olcSqlConcatPattern' "
92 			"DESC 'Pattern used to concatenate strings' "
93 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
94 	{ "subtree_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
95 		(void *)offsetof(struct backsql_info, sql_subtree_cond),
96 		"( OLcfgDbAt:6.21 NAME 'olcSqlSubtreeCond' "
97 			"DESC 'Where-clause template for a subtree search condition' "
98 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
99 	{ "children_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
100 		(void *)offsetof(struct backsql_info, sql_children_cond),
101 		"( OLcfgDbAt:6.22 NAME 'olcSqlChildrenCond' "
102 			"DESC 'Where-clause template for a children search condition' "
103 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
104 	{ "dn_match_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
105 		(void *)offsetof(struct backsql_info, sql_dn_match_cond),
106 		"( OLcfgDbAt:6.23 NAME 'olcSqlDnMatchCond' "
107 			"DESC 'Where-clause template for a DN match search condition' "
108 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
109 	{ "oc_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
110 		(void *)offsetof(struct backsql_info, sql_oc_query),
111 		"( OLcfgDbAt:6.24 NAME 'olcSqlOcQuery' "
112 			"DESC 'Query used to collect objectClass mapping data' "
113 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
114 	{ "at_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
115 		(void *)offsetof(struct backsql_info, sql_at_query),
116 		"( OLcfgDbAt:6.25 NAME 'olcSqlAtQuery' "
117 			"DESC 'Query used to collect attributeType mapping data' "
118 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
119 	{ "insentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
120 		(void *)offsetof(struct backsql_info, sql_insentry_stmt),
121 		"( OLcfgDbAt:6.26 NAME 'olcSqlInsEntryStmt' "
122 			"DESC 'Statement used to insert a new entry' "
123 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
124 	{ "create_needs_select", "yes|no", 2, 2, 0,
125 		ARG_ON_OFF|ARG_MAGIC|BSQL_CREATE_NEEDS_SEL, (void *)sql_cf_gen,
126 		"( OLcfgDbAt:6.27 NAME 'olcSqlCreateNeedsSelect' "
127 			"DESC 'Whether entry creation needs a subsequent select' "
128 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
129 	{ "upper_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
130 		(void *)offsetof(struct backsql_info, sql_upper_func),
131 		"( OLcfgDbAt:6.28 NAME 'olcSqlUpperFunc' "
132 			"DESC 'Function that converts a value to uppercase' "
133 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
134 	{ "upper_needs_cast", "yes|no", 2, 2, 0,
135 		ARG_ON_OFF|ARG_MAGIC|BSQL_UPPER_NEEDS_CAST, (void *)sql_cf_gen,
136 		"( OLcfgDbAt:6.29 NAME 'olcSqlUpperNeedsCast' "
137 			"DESC 'Whether olcSqlUpperFunc needs an explicit cast' "
138 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
139 	{ "strcast_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
140 		(void *)offsetof(struct backsql_info, sql_strcast_func),
141 		"( OLcfgDbAt:6.30 NAME 'olcSqlStrcastFunc' "
142 			"DESC 'Function that converts a value to a string' "
143 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
144 	{ "delentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
145 		(void *)offsetof(struct backsql_info, sql_delentry_stmt),
146 		"( OLcfgDbAt:6.31 NAME 'olcSqlDelEntryStmt' "
147 			"DESC 'Statement used to delete an existing entry' "
148 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
149 	{ "renentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
150 		(void *)offsetof(struct backsql_info, sql_renentry_stmt),
151 		"( OLcfgDbAt:6.32 NAME 'olcSqlRenEntryStmt' "
152 			"DESC 'Statement used to rename an entry' "
153 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
154 	{ "delobjclasses_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
155 		(void *)offsetof(struct backsql_info, sql_delobjclasses_stmt),
156 		"( OLcfgDbAt:6.33 NAME 'olcSqlDelObjclassesStmt' "
157 			"DESC 'Statement used to delete the ID of an entry' "
158 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
159 	{ "has_ldapinfo_dn_ru", "yes|no", 2, 2, 0,
160 		ARG_ON_OFF|ARG_MAGIC|BSQL_HAS_LDAPINFO_DN_RU, (void *)sql_cf_gen,
161 		"( OLcfgDbAt:6.34 NAME 'olcSqlHasLDAPinfoDnRu' "
162 			"DESC 'Whether the dn_ru column is present' "
163 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
164 	{ "fail_if_no_mapping", "yes|no", 2, 2, 0,
165 		ARG_ON_OFF|ARG_MAGIC|BSQL_FAIL_IF_NO_MAPPING, (void *)sql_cf_gen,
166 		"( OLcfgDbAt:6.35 NAME 'olcSqlFailIfNoMapping' "
167 			"DESC 'Whether to fail on unknown attribute mappings' "
168 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
169 	{ "allow_orphans", "yes|no", 2, 2, 0,
170 		ARG_ON_OFF|ARG_MAGIC|BSQL_ALLOW_ORPHANS, (void *)sql_cf_gen,
171 		"( OLcfgDbAt:6.36 NAME 'olcSqlAllowOrphans' "
172 			"DESC 'Whether to allow adding entries with no parent' "
173 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
174 	{ "baseobject", "[file]", 1, 2, 0,
175 		ARG_STRING|ARG_MAGIC|BSQL_BASE_OBJECT, (void *)sql_cf_gen,
176 		"( OLcfgDbAt:6.37 NAME 'olcSqlBaseObject' "
177 			"DESC 'Manage an in-memory baseObject entry' "
178 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
179 	{ "sqllayer", "name", 2, 0, 0,
180 		ARG_MAGIC|BSQL_LAYER, (void *)sql_cf_gen,
181 		"( OLcfgDbAt:6.38 NAME 'olcSqlLayer' "
182 			"DESC 'Helper used to map DNs between LDAP and SQL' "
183 			"SYNTAX OMsDirectoryString )", NULL, NULL },
184 	{ "use_subtree_shortcut", "yes|no", 2, 2, 0,
185 		ARG_ON_OFF|ARG_MAGIC|BSQL_SUBTREE_SHORTCUT, (void *)sql_cf_gen,
186 		"( OLcfgDbAt:6.39 NAME 'olcSqlUseSubtreeShortcut' "
187 			"DESC 'Collect all entries when searchBase is DB suffix' "
188 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
189 	{ "fetch_all_attrs", "yes|no", 2, 2, 0,
190 		ARG_ON_OFF|ARG_MAGIC|BSQL_FETCH_ALL_ATTRS, (void *)sql_cf_gen,
191 		"( OLcfgDbAt:6.40 NAME 'olcSqlFetchAllAttrs' "
192 			"DESC 'Require all attributes to always be loaded' "
193 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
194 	{ "fetch_attrs", "attrlist", 2, 0, 0,
195 		ARG_MAGIC|BSQL_FETCH_ATTRS, (void *)sql_cf_gen,
196 		"( OLcfgDbAt:6.41 NAME 'olcSqlFetchAttrs' "
197 			"DESC 'Set of attributes to always fetch' "
198 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
199 	{ "check_schema", "yes|no", 2, 2, 0,
200 		ARG_ON_OFF|ARG_MAGIC|BSQL_CHECK_SCHEMA, (void *)sql_cf_gen,
201 		"( OLcfgDbAt:6.42 NAME 'olcSqlCheckSchema' "
202 			"DESC 'Check schema after modifications' "
203 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
204 	{ "aliasing_keyword", "string", 2, 2, 0,
205 		ARG_STRING|ARG_MAGIC|BSQL_ALIASING_KEYWORD, (void *)sql_cf_gen,
206 		"( OLcfgDbAt:6.43 NAME 'olcSqlAliasingKeyword' "
207 			"DESC 'The aliasing keyword' "
208 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
209 	{ "aliasing_quote", "string", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
210 		(void *)offsetof(struct backsql_info, sql_aliasing_quote),
211 		"( OLcfgDbAt:6.44 NAME 'olcSqlAliasingQuote' "
212 			"DESC 'Quoting char of the aliasing keyword' "
213 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
214 	{ "autocommit", "yes|no", 2, 2, 0,
215 		ARG_ON_OFF|ARG_MAGIC|SQL_AUTOCOMMIT, (void *)sql_cf_gen,
216 		"( OLcfgDbAt:6.45 NAME 'olcSqlAutocommit' "
217 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
218 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
219 		NULL, NULL, NULL, NULL }
220 };
221 
222 static ConfigOCs sqlocs[] = {
223 	{
224 		"( OLcfgDbOc:6.1 "
225 		"NAME 'olcSqlConfig' "
226 		"DESC 'SQL backend configuration' "
227 		"SUP olcDatabaseConfig "
228 		"MUST olcDbName "
229 		"MAY ( olcDbHost $ olcDbUser $ olcDbPass $ olcSqlConcatPattern $ "
230 		"olcSqlSubtreeCond $ olcsqlChildrenCond $ olcSqlDnMatchCond $ "
231 		"olcSqlOcQuery $ olcSqlAtQuery $ olcSqlInsEntryStmt $ "
232 		"olcSqlCreateNeedsSelect $ olcSqlUpperFunc $ olcSqlUpperNeedsCast $ "
233 		"olcSqlStrCastFunc $ olcSqlDelEntryStmt $ olcSqlRenEntryStmt $ "
234 		"olcSqlDelObjClassesStmt $ olcSqlHasLDAPInfoDnRu $ "
235 		"olcSqlFailIfNoMapping $ olcSqlAllowOrphans $ olcSqlBaseObject $ "
236 		"olcSqlLayer $ olcSqlUseSubtreeShortcut $ olcSqlFetchAllAttrs $ "
237 		"olcSqlFetchAttrs $ olcSqlCheckSchema $ olcSqlAliasingKeyword $ "
238 		"olcSqlAliasingQuote $ olcSqlAutocommit ) )",
239 			Cft_Database, sqlcfg },
240 	{ NULL, Cft_Abstract, NULL }
241 };
242 
243 static int
244 sql_cf_gen( ConfigArgs *c )
245 {
246 	backsql_info 	*bi = (backsql_info *)c->be->be_private;
247 	int rc = 0;
248 
249 	if ( c->op == SLAP_CONFIG_EMIT ) {
250 		switch( c->type ) {
251 		case BSQL_CONCAT_PATT:
252 			if ( bi->sql_concat_patt ) {
253 				c->value_string = ch_strdup( bi->sql_concat_patt );
254 			} else {
255 				rc = 1;
256 			}
257 			break;
258 		case BSQL_CREATE_NEEDS_SEL:
259 			if ( bi->sql_flags & BSQLF_CREATE_NEEDS_SELECT )
260 				c->value_int = 1;
261 			break;
262 		case BSQL_UPPER_NEEDS_CAST:
263 			if ( bi->sql_flags & BSQLF_UPPER_NEEDS_CAST )
264 				c->value_int = 1;
265 			break;
266 		case BSQL_HAS_LDAPINFO_DN_RU:
267 			if ( !(bi->sql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU) )
268 				return 1;
269 			if ( bi->sql_flags & BSQLF_HAS_LDAPINFO_DN_RU )
270 				c->value_int = 1;
271 			break;
272 		case BSQL_FAIL_IF_NO_MAPPING:
273 			if ( bi->sql_flags & BSQLF_FAIL_IF_NO_MAPPING )
274 				c->value_int = 1;
275 			break;
276 		case BSQL_ALLOW_ORPHANS:
277 			if ( bi->sql_flags & BSQLF_ALLOW_ORPHANS )
278 				c->value_int = 1;
279 			break;
280 		case BSQL_SUBTREE_SHORTCUT:
281 			if ( bi->sql_flags & BSQLF_USE_SUBTREE_SHORTCUT )
282 				c->value_int = 1;
283 			break;
284 		case BSQL_FETCH_ALL_ATTRS:
285 			if ( bi->sql_flags & BSQLF_FETCH_ALL_ATTRS )
286 				c->value_int = 1;
287 			break;
288 		case BSQL_CHECK_SCHEMA:
289 			if ( bi->sql_flags & BSQLF_CHECK_SCHEMA )
290 				c->value_int = 1;
291 			break;
292 		case BSQL_AUTOCOMMIT:
293 			if ( bi->sql_flags & BSQLF_AUTOCOMMIT_ON )
294 				c->value_int = 1;
295 			break;
296 		case BSQL_BASE_OBJECT:
297 			if ( bi->sql_base_ob_file ) {
298 				c->value_string = ch_strdup( bi->sql_base_ob_file );
299 			} else if ( bi->sql_baseObject ) {
300 				c->value_string = ch_strdup( "TRUE" );
301 			} else {
302 				rc = 1;
303 			}
304 			break;
305 		case BSQL_LAYER:
306 			if ( bi->sql_api ) {
307 				backsql_api *ba;
308 				struct berval bv;
309 				char *ptr;
310 				int i;
311 				for ( ba = bi->sql_api; ba; ba = ba->ba_next ) {
312 					bv.bv_len = strlen( ba->ba_name );
313 					if ( ba->ba_argc ) {
314 						for ( i = 0; i<ba->ba_argc; i++ )
315 							bv.bv_len += strlen( ba->ba_argv[i] ) + 3;
316 					}
317 					bv.bv_val = ch_malloc( bv.bv_len + 1 );
318 					ptr = lutil_strcopy( bv.bv_val, ba->ba_name );
319 					if ( ba->ba_argc ) {
320 						for ( i = 0; i<ba->ba_argc; i++ ) {
321 							*ptr++ = ' ';
322 							*ptr++ = '"';
323 							ptr = lutil_strcopy( ptr, ba->ba_argv[i] );
324 							*ptr++ = '"';
325 						}
326 					}
327 					ber_bvarray_add( &c->rvalue_vals, &bv );
328 				}
329 			} else {
330 				rc = 1;
331 			}
332 			break;
333 		case BSQL_ALIASING_KEYWORD:
334 			if ( !BER_BVISNULL( &bi->sql_aliasing )) {
335 				struct berval bv;
336 				bv = bi->sql_aliasing;
337 				bv.bv_len--;
338 				value_add_one( &c->rvalue_vals, &bv );
339 			} else {
340 				rc = 1;
341 			}
342 			break;
343 		case BSQL_FETCH_ATTRS:
344 			if ( bi->sql_anlist ||
345 				( bi->sql_flags & (BSQLF_FETCH_ALL_USERATTRS|
346 								   BSQLF_FETCH_ALL_OPATTRS)))
347 			{
348 				char buf[BUFSIZ*2], *ptr;
349 				struct berval bv;
350 #   define WHATSLEFT    ((ber_len_t) (&buf[sizeof( buf )] - ptr))
351 				ptr = buf;
352 				if ( bi->sql_anlist ) {
353 					ptr = anlist_unparse( bi->sql_anlist, ptr, WHATSLEFT );
354 					if ( ptr == NULL )
355 						return 1;
356 				}
357 				if ( bi->sql_flags & BSQLF_FETCH_ALL_USERATTRS ) {
358 					if ( WHATSLEFT <= STRLENOF( ",*" )) return 1;
359 					if ( ptr != buf ) *ptr++ = ',';
360 					*ptr++ = '*';
361 				}
362 				if ( bi->sql_flags & BSQLF_FETCH_ALL_OPATTRS ) {
363 					if ( WHATSLEFT <= STRLENOF( ",+" )) return 1;
364 					if ( ptr != buf ) *ptr++ = ',';
365 					*ptr++ = '+';
366 				}
367 				bv.bv_val = buf;
368 				bv.bv_len = ptr - buf;
369 				value_add_one( &c->rvalue_vals, &bv );
370 			}
371 			break;
372 		}
373 		return rc;
374 	} else if ( c->op == LDAP_MOD_DELETE ) {	/* FIXME */
375 		return -1;
376 	}
377 
378 	switch( c->type ) {
379 	case BSQL_CONCAT_PATT:
380 		if ( backsql_split_pattern( c->argv[ 1 ], &bi->sql_concat_func, 2 ) ) {
381 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
382 				"%s: unable to parse pattern \"%s\"",
383 				c->log, c->argv[ 1 ] );
384 			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
385 			return -1;
386 		}
387 		bi->sql_concat_patt = c->value_string;
388 		break;
389 	case BSQL_CREATE_NEEDS_SEL:
390 		if ( c->value_int )
391 			bi->sql_flags |= BSQLF_CREATE_NEEDS_SELECT;
392 		else
393 			bi->sql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
394 		break;
395 	case BSQL_UPPER_NEEDS_CAST:
396 		if ( c->value_int )
397 			bi->sql_flags |= BSQLF_UPPER_NEEDS_CAST;
398 		else
399 			bi->sql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
400 		break;
401 	case BSQL_HAS_LDAPINFO_DN_RU:
402 		bi->sql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
403 		if ( c->value_int )
404 			bi->sql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
405 		else
406 			bi->sql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
407 		break;
408 	case BSQL_FAIL_IF_NO_MAPPING:
409 		if ( c->value_int )
410 			bi->sql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
411 		else
412 			bi->sql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
413 		break;
414 	case BSQL_ALLOW_ORPHANS:
415 		if ( c->value_int )
416 			bi->sql_flags |= BSQLF_ALLOW_ORPHANS;
417 		else
418 			bi->sql_flags &= ~BSQLF_ALLOW_ORPHANS;
419 		break;
420 	case BSQL_SUBTREE_SHORTCUT:
421 		if ( c->value_int )
422 			bi->sql_flags |= BSQLF_USE_SUBTREE_SHORTCUT;
423 		else
424 			bi->sql_flags &= ~BSQLF_USE_SUBTREE_SHORTCUT;
425 		break;
426 	case BSQL_FETCH_ALL_ATTRS:
427 		if ( c->value_int )
428 			bi->sql_flags |= BSQLF_FETCH_ALL_ATTRS;
429 		else
430 			bi->sql_flags &= ~BSQLF_FETCH_ALL_ATTRS;
431 		break;
432 	case BSQL_CHECK_SCHEMA:
433 		if ( c->value_int )
434 			bi->sql_flags |= BSQLF_CHECK_SCHEMA;
435 		else
436 			bi->sql_flags &= ~BSQLF_CHECK_SCHEMA;
437 		break;
438 	case BSQL_AUTOCOMMIT:
439 		if ( c->value_int )
440 			bi->sql_flags |= BSQLF_AUTOCOMMIT_ON;
441 		else
442 			bi->sql_flags &= ~BSQLF_AUTOCOMMIT_ON;
443 		break;
444 	case BSQL_BASE_OBJECT:
445 		if ( c->be->be_nsuffix == NULL ) {
446 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
447 				"%s: suffix must be set", c->log );
448 			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
449 			rc = ARG_BAD_CONF;
450 			break;
451 		}
452 		if ( bi->sql_baseObject ) {
453 			Debug( LDAP_DEBUG_CONFIG,
454 				"%s: "
455 				"\"baseObject\" already provided (will be overwritten)\n",
456 				c->log, 0, 0 );
457 			entry_free( bi->sql_baseObject );
458 		}
459 		if ( c->argc == 2 && !strcmp( c->argv[1], "TRUE" ))
460 			c->argc = 1;
461 		switch( c->argc ) {
462 		case 1:
463 			return create_baseObject( c->be, c->fname, c->lineno );
464 
465 		case 2:
466 			rc = read_baseObject( c->be, c->argv[ 1 ] );
467 			if ( rc == 0 ) {
468 				ch_free( bi->sql_base_ob_file );
469 				bi->sql_base_ob_file = c->value_string;
470 			}
471 			return rc;
472 
473 		default:
474 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
475 				"%s: trailing values in directive", c->log );
476 			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
477 			return 1;
478 		}
479 		break;
480 	case BSQL_LAYER:
481 		if ( backsql_api_config( bi, c->argv[ 1 ], c->argc - 2, &c->argv[ 2 ] ) )
482 		{
483 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
484 				"%s: unable to load sql layer", c->log );
485 			Debug( LDAP_DEBUG_ANY, "%s \"%s\"\n",
486 				c->cr_msg, c->argv[1], 0 );
487 			return 1;
488 		}
489 		break;
490 	case BSQL_ALIASING_KEYWORD:
491 		if ( ! BER_BVISNULL( &bi->sql_aliasing ) ) {
492 			ch_free( bi->sql_aliasing.bv_val );
493 		}
494 
495 		ber_str2bv( c->argv[ 1 ], strlen( c->argv[ 1 ] ) + 1, 1,
496 			&bi->sql_aliasing );
497 		/* add a trailing space... */
498 		bi->sql_aliasing.bv_val[ bi->sql_aliasing.bv_len - 1] = ' ';
499 		break;
500 	case BSQL_FETCH_ATTRS: {
501 		char		*str, *s, *next;
502 		const char	*delimstr = ",";
503 
504 		str = ch_strdup( c->argv[ 1 ] );
505 		for ( s = ldap_pvt_strtok( str, delimstr, &next );
506 				s != NULL;
507 				s = ldap_pvt_strtok( NULL, delimstr, &next ) )
508 		{
509 			if ( strlen( s ) == 1 ) {
510 				if ( *s == '*' ) {
511 					bi->sql_flags |= BSQLF_FETCH_ALL_USERATTRS;
512 					c->argv[ 1 ][ s - str ] = ',';
513 
514 				} else if ( *s == '+' ) {
515 					bi->sql_flags |= BSQLF_FETCH_ALL_OPATTRS;
516 					c->argv[ 1 ][ s - str ] = ',';
517 				}
518 			}
519 		}
520 		ch_free( str );
521 		bi->sql_anlist = str2anlist( bi->sql_anlist, c->argv[ 1 ], delimstr );
522 		if ( bi->sql_anlist == NULL ) {
523 			return -1;
524 		}
525 		}
526 		break;
527 	}
528 	return rc;
529 }
530 
531 /*
532  * Read the entries specified in fname and merge the attributes
533  * to the user defined baseObject entry. Note that if we find any errors
534  * what so ever, we will discard the entire entries, print an
535  * error message and return.
536  */
537 static int
538 read_baseObject(
539 	BackendDB	*be,
540 	const char	*fname )
541 {
542 	backsql_info 	*bi = (backsql_info *)be->be_private;
543 	LDIFFP		*fp;
544 	int		rc = 0, lmax = 0, ldifrc;
545 	unsigned long	lineno = 0;
546 	char		*buf = NULL;
547 
548 	assert( fname != NULL );
549 
550 	fp = ldif_open( fname, "r" );
551 	if ( fp == NULL ) {
552 		Debug( LDAP_DEBUG_ANY,
553 			"could not open back-sql baseObject "
554 			"attr file \"%s\" - absolute path?\n",
555 			fname, 0, 0 );
556 		perror( fname );
557 		return LDAP_OTHER;
558 	}
559 
560 	bi->sql_baseObject = entry_alloc();
561 	if ( bi->sql_baseObject == NULL ) {
562 		Debug( LDAP_DEBUG_ANY,
563 			"read_baseObject_file: entry_alloc failed", 0, 0, 0 );
564 		ldif_close( fp );
565 		return LDAP_NO_MEMORY;
566 	}
567 	bi->sql_baseObject->e_name = be->be_suffix[0];
568 	bi->sql_baseObject->e_nname = be->be_nsuffix[0];
569 	bi->sql_baseObject->e_attrs = NULL;
570 
571 	while (( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
572 		Entry		*e = str2entry( buf );
573 		Attribute	*a;
574 
575 		if( e == NULL ) {
576 			fprintf( stderr, "back-sql baseObject: "
577 					"could not parse entry (line=%lu)\n",
578 					lineno );
579 			rc = LDAP_OTHER;
580 			break;
581 		}
582 
583 		/* make sure the DN is the database's suffix */
584 		if ( !be_issuffix( be, &e->e_nname ) ) {
585 			fprintf( stderr,
586 				"back-sql: invalid baseObject - "
587 				"dn=\"%s\" (line=%lu)\n",
588 				e->e_name.bv_val, lineno );
589 			entry_free( e );
590 			rc = LDAP_OTHER;
591 			break;
592 		}
593 
594 		/*
595 		 * we found a valid entry, so walk thru all the attributes in the
596 		 * entry, and add each attribute type and description to baseObject
597 		 */
598 		for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
599 			if ( attr_merge( bi->sql_baseObject, a->a_desc,
600 						a->a_vals,
601 						( a->a_nvals == a->a_vals ) ?
602 						NULL : a->a_nvals ) )
603 			{
604 				rc = LDAP_OTHER;
605 				break;
606 			}
607 		}
608 
609 		entry_free( e );
610 		if ( rc ) {
611 			break;
612 		}
613 	}
614 
615 	if ( ldifrc < 0 )
616 		rc = LDAP_OTHER;
617 
618 	if ( rc ) {
619 		entry_free( bi->sql_baseObject );
620 		bi->sql_baseObject = NULL;
621 	}
622 
623 	ch_free( buf );
624 
625 	ldif_close( fp );
626 
627 	Debug( LDAP_DEBUG_CONFIG, "back-sql baseObject file \"%s\" read.\n",
628 			fname, 0, 0 );
629 
630 	return rc;
631 }
632 
633 static int
634 create_baseObject(
635 	BackendDB	*be,
636 	const char	*fname,
637 	int		lineno )
638 {
639 	backsql_info 	*bi = (backsql_info *)be->be_private;
640 	LDAPRDN		rdn;
641 	char		*p;
642 	int		rc, iAVA;
643 	char		buf[1024];
644 
645 	snprintf( buf, sizeof(buf),
646 			"dn: %s\n"
647 			"objectClass: extensibleObject\n"
648 			"description: builtin baseObject for back-sql\n"
649 			"description: all entries mapped "
650 				"in table \"ldap_entries\" "
651 				"must have "
652 				"\"" BACKSQL_BASEOBJECT_IDSTR "\" "
653 				"in the \"parent\" column",
654 			be->be_suffix[0].bv_val );
655 
656 	bi->sql_baseObject = str2entry( buf );
657 	if ( bi->sql_baseObject == NULL ) {
658 		Debug( LDAP_DEBUG_TRACE,
659 			"<==backsql_db_config (%s line %d): "
660 			"unable to parse baseObject entry\n",
661 			fname, lineno, 0 );
662 		return 1;
663 	}
664 
665 	if ( BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ) {
666 		return 0;
667 	}
668 
669 	rc = ldap_bv2rdn( &be->be_suffix[ 0 ], &rdn, (char **)&p,
670 			LDAP_DN_FORMAT_LDAP );
671 	if ( rc != LDAP_SUCCESS ) {
672 		snprintf( buf, sizeof(buf),
673 			"unable to extract RDN "
674 			"from baseObject DN \"%s\" (%d: %s)",
675 			be->be_suffix[ 0 ].bv_val,
676 			rc, ldap_err2string( rc ) );
677 		Debug( LDAP_DEBUG_TRACE,
678 			"<==backsql_db_config (%s line %d): %s\n",
679 			fname, lineno, buf );
680 		return 1;
681 	}
682 
683 	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
684 		LDAPAVA				*ava = rdn[ iAVA ];
685 		AttributeDescription		*ad = NULL;
686 		slap_syntax_transform_func	*transf = NULL;
687 		struct berval			bv = BER_BVNULL;
688 		const char			*text = NULL;
689 
690 		assert( ava != NULL );
691 
692 		rc = slap_bv2ad( &ava->la_attr, &ad, &text );
693 		if ( rc != LDAP_SUCCESS ) {
694 			snprintf( buf, sizeof(buf),
695 				"AttributeDescription of naming "
696 				"attribute #%d from baseObject "
697 				"DN \"%s\": %d: %s",
698 				iAVA, be->be_suffix[ 0 ].bv_val,
699 				rc, ldap_err2string( rc ) );
700 			Debug( LDAP_DEBUG_TRACE,
701 				"<==backsql_db_config (%s line %d): %s\n",
702 				fname, lineno, buf );
703 			return 1;
704 		}
705 
706 		transf = ad->ad_type->sat_syntax->ssyn_pretty;
707 		if ( transf ) {
708 			/*
709 	 		 * transform value by pretty function
710 			 *	if value is empty, use empty_bv
711 			 */
712 			rc = ( *transf )( ad->ad_type->sat_syntax,
713 				ava->la_value.bv_len
714 					? &ava->la_value
715 					: (struct berval *) &slap_empty_bv,
716 				&bv, NULL );
717 
718 			if ( rc != LDAP_SUCCESS ) {
719 				snprintf( buf, sizeof(buf),
720 					"prettying of attribute #%d "
721 					"from baseObject "
722 					"DN \"%s\" failed: %d: %s",
723 					iAVA, be->be_suffix[ 0 ].bv_val,
724 					rc, ldap_err2string( rc ) );
725 				Debug( LDAP_DEBUG_TRACE,
726 					"<==backsql_db_config (%s line %d): "
727 					"%s\n",
728 					fname, lineno, buf );
729 				return 1;
730 			}
731 		}
732 
733 		if ( !BER_BVISNULL( &bv ) ) {
734 			if ( ava->la_flags & LDAP_AVA_FREE_VALUE ) {
735 				ber_memfree( ava->la_value.bv_val );
736 			}
737 			ava->la_value = bv;
738 			ava->la_flags |= LDAP_AVA_FREE_VALUE;
739 		}
740 
741 		attr_merge_normalize_one( bi->sql_baseObject,
742 				ad, &ava->la_value, NULL );
743 	}
744 
745 	ldap_rdnfree( rdn );
746 
747 	return 0;
748 }
749 
750 int backsql_init_cf( BackendInfo *bi )
751 {
752 	int rc;
753 
754 	bi->bi_cf_ocs = sqlocs;
755 	rc = config_register_schema( sqlcfg, sqlocs );
756 	if ( rc ) return rc;
757 	return 0;
758 }
759