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