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