config.c revision 1.1.1.7
1/*	$NetBSD: config.c,v 1.1.1.7 2019/08/08 13:31:45 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1999-2019 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.1.1.7 2019/08/08 13:31:45 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
41static int
42create_baseObject(
43	BackendDB	*be,
44	const char	*fname,
45	int		lineno );
46
47static int
48read_baseObject(
49	BackendDB	*be,
50	const char	*fname );
51
52static ConfigDriver sql_cf_gen;
53
54enum {
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
71static 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
230static 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
251static int
252sql_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 */
545static int
546read_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
641static int
642create_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
758int 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