]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/config.c
Happy New Year
[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-2018 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|BSQL_AUTOCOMMIT, (void *)sql_cf_gen,
214                 "( OLcfgDbAt:6.45 NAME 'olcSqlAutocommit' "
215                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
216         { "id_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
217                 (void *)offsetof(struct backsql_info, sql_id_query),
218                 "( OLcfgDbAt:6.46 NAME 'olcSqlIdQuery' "
219                         "DESC 'Query used to collect entryID mapping data' "
220                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
221         { NULL, NULL, 0, 0, 0, ARG_IGNORED,
222                 NULL, NULL, NULL, NULL }
223 };
224
225 static ConfigOCs sqlocs[] = {
226         {
227                 "( OLcfgDbOc:6.1 "
228                 "NAME 'olcSqlConfig' "
229                 "DESC 'SQL backend configuration' "
230                 "SUP olcDatabaseConfig "
231                 "MUST olcDbName "
232                 "MAY ( olcDbHost $ olcDbUser $ olcDbPass $ olcSqlConcatPattern $ "
233                 "olcSqlSubtreeCond $ olcsqlChildrenCond $ olcSqlDnMatchCond $ "
234                 "olcSqlOcQuery $ olcSqlAtQuery $ olcSqlInsEntryStmt $ "
235                 "olcSqlCreateNeedsSelect $ olcSqlUpperFunc $ olcSqlUpperNeedsCast $ "
236                 "olcSqlStrCastFunc $ olcSqlDelEntryStmt $ olcSqlRenEntryStmt $ "
237                 "olcSqlDelObjClassesStmt $ olcSqlHasLDAPInfoDnRu $ "
238                 "olcSqlFailIfNoMapping $ olcSqlAllowOrphans $ olcSqlBaseObject $ "
239                 "olcSqlLayer $ olcSqlUseSubtreeShortcut $ olcSqlFetchAllAttrs $ "
240                 "olcSqlFetchAttrs $ olcSqlCheckSchema $ olcSqlAliasingKeyword $ "
241                 "olcSqlAliasingQuote $ olcSqlAutocommit $ olcSqlIdQuery ) )",
242                         Cft_Database, sqlcfg },
243         { NULL, Cft_Abstract, NULL }
244 };
245
246 static int
247 sql_cf_gen( ConfigArgs *c )
248 {
249         backsql_info    *bi = (backsql_info *)c->be->be_private;
250         int rc = 0;
251
252         if ( c->op == SLAP_CONFIG_EMIT ) {
253                 switch( c->type ) {
254                 case BSQL_CONCAT_PATT:
255                         if ( bi->sql_concat_patt ) {
256                                 c->value_string = ch_strdup( bi->sql_concat_patt );
257                         } else {
258                                 rc = 1;
259                         }
260                         break;
261                 case BSQL_CREATE_NEEDS_SEL:
262                         if ( bi->sql_flags & BSQLF_CREATE_NEEDS_SELECT )
263                                 c->value_int = 1;
264                         break;
265                 case BSQL_UPPER_NEEDS_CAST:
266                         if ( bi->sql_flags & BSQLF_UPPER_NEEDS_CAST )
267                                 c->value_int = 1;
268                         break;
269                 case BSQL_HAS_LDAPINFO_DN_RU:
270                         if ( !(bi->sql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU) )
271                                 return 1;
272                         if ( bi->sql_flags & BSQLF_HAS_LDAPINFO_DN_RU )
273                                 c->value_int = 1;
274                         break;
275                 case BSQL_FAIL_IF_NO_MAPPING:
276                         if ( bi->sql_flags & BSQLF_FAIL_IF_NO_MAPPING )
277                                 c->value_int = 1;
278                         break;
279                 case BSQL_ALLOW_ORPHANS:
280                         if ( bi->sql_flags & BSQLF_ALLOW_ORPHANS )
281                                 c->value_int = 1;
282                         break;
283                 case BSQL_SUBTREE_SHORTCUT:
284                         if ( bi->sql_flags & BSQLF_USE_SUBTREE_SHORTCUT )
285                                 c->value_int = 1;
286                         break;
287                 case BSQL_FETCH_ALL_ATTRS:
288                         if ( bi->sql_flags & BSQLF_FETCH_ALL_ATTRS )
289                                 c->value_int = 1;
290                         break;
291                 case BSQL_CHECK_SCHEMA:
292                         if ( bi->sql_flags & BSQLF_CHECK_SCHEMA )
293                                 c->value_int = 1;
294                         break;
295                 case BSQL_AUTOCOMMIT:
296                         if ( bi->sql_flags & BSQLF_AUTOCOMMIT_ON )
297                                 c->value_int = 1;
298                         break;
299                 case BSQL_BASE_OBJECT:
300                         if ( bi->sql_base_ob_file ) {
301                                 c->value_string = ch_strdup( bi->sql_base_ob_file );
302                         } else if ( bi->sql_baseObject ) {
303                                 c->value_string = ch_strdup( "TRUE" );
304                         } else {
305                                 rc = 1;
306                         }
307                         break;
308                 case BSQL_LAYER:
309                         if ( bi->sql_api ) {
310                                 backsql_api *ba;
311                                 struct berval bv;
312                                 char *ptr;
313                                 int i;
314                                 for ( ba = bi->sql_api; ba; ba = ba->ba_next ) {
315                                         bv.bv_len = strlen( ba->ba_name );
316                                         if ( ba->ba_argc ) {
317                                                 for ( i = 0; i<ba->ba_argc; i++ )
318                                                         bv.bv_len += strlen( ba->ba_argv[i] ) + 3;
319                                         }
320                                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
321                                         ptr = lutil_strcopy( bv.bv_val, ba->ba_name );
322                                         if ( ba->ba_argc ) {
323                                                 for ( i = 0; i<ba->ba_argc; i++ ) {
324                                                         *ptr++ = ' ';
325                                                         *ptr++ = '"';
326                                                         ptr = lutil_strcopy( ptr, ba->ba_argv[i] );
327                                                         *ptr++ = '"';
328                                                 }
329                                         }
330                                         ber_bvarray_add( &c->rvalue_vals, &bv );
331                                 }
332                         } else {
333                                 rc = 1;
334                         }
335                         break;
336                 case BSQL_ALIASING_KEYWORD:
337                         if ( !BER_BVISNULL( &bi->sql_aliasing )) {
338                                 struct berval bv;
339                                 bv = bi->sql_aliasing;
340                                 bv.bv_len--;
341                                 value_add_one( &c->rvalue_vals, &bv );
342                         } else {
343                                 rc = 1;
344                         }
345                         break;
346                 case BSQL_FETCH_ATTRS:
347                         if ( bi->sql_anlist ||
348                                 ( bi->sql_flags & (BSQLF_FETCH_ALL_USERATTRS|
349                                                                    BSQLF_FETCH_ALL_OPATTRS)))
350                         {
351                                 char buf[BUFSIZ*2], *ptr;
352                                 struct berval bv;
353 #   define WHATSLEFT    ((ber_len_t) (&buf[sizeof( buf )] - ptr))
354                                 ptr = buf;
355                                 if ( bi->sql_anlist ) {
356                                         ptr = anlist_unparse( bi->sql_anlist, ptr, WHATSLEFT );
357                                         if ( ptr == NULL )
358                                                 return 1;
359                                 }
360                                 if ( bi->sql_flags & BSQLF_FETCH_ALL_USERATTRS ) {
361                                         if ( WHATSLEFT <= STRLENOF( ",*" )) return 1;
362                                         if ( ptr != buf ) *ptr++ = ',';
363                                         *ptr++ = '*';
364                                 }
365                                 if ( bi->sql_flags & BSQLF_FETCH_ALL_OPATTRS ) {
366                                         if ( WHATSLEFT <= STRLENOF( ",+" )) return 1;
367                                         if ( ptr != buf ) *ptr++ = ',';
368                                         *ptr++ = '+';
369                                 }
370                                 bv.bv_val = buf;
371                                 bv.bv_len = ptr - buf;
372                                 value_add_one( &c->rvalue_vals, &bv );
373                         }
374                         break;
375                 }
376                 return rc;
377         } else if ( c->op == LDAP_MOD_DELETE ) {        /* FIXME */
378                 return -1;
379         }
380
381         switch( c->type ) {
382         case BSQL_CONCAT_PATT:
383                 if ( backsql_split_pattern( c->argv[ 1 ], &bi->sql_concat_func, 2 ) ) {
384                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
385                                 "%s: unable to parse pattern \"%s\"",
386                                 c->log, c->argv[ 1 ] );
387                         Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
388                         return -1;
389                 }
390                 bi->sql_concat_patt = c->value_string;
391                 break;
392         case BSQL_CREATE_NEEDS_SEL:
393                 if ( c->value_int )
394                         bi->sql_flags |= BSQLF_CREATE_NEEDS_SELECT;
395                 else
396                         bi->sql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
397                 break;
398         case BSQL_UPPER_NEEDS_CAST:
399                 if ( c->value_int )
400                         bi->sql_flags |= BSQLF_UPPER_NEEDS_CAST;
401                 else
402                         bi->sql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
403                 break;
404         case BSQL_HAS_LDAPINFO_DN_RU:
405                 bi->sql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
406                 if ( c->value_int )
407                         bi->sql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
408                 else
409                         bi->sql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
410                 break;
411         case BSQL_FAIL_IF_NO_MAPPING:
412                 if ( c->value_int )
413                         bi->sql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
414                 else
415                         bi->sql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
416                 break;
417         case BSQL_ALLOW_ORPHANS:
418                 if ( c->value_int )
419                         bi->sql_flags |= BSQLF_ALLOW_ORPHANS;
420                 else
421                         bi->sql_flags &= ~BSQLF_ALLOW_ORPHANS;
422                 break;
423         case BSQL_SUBTREE_SHORTCUT:
424                 if ( c->value_int )
425                         bi->sql_flags |= BSQLF_USE_SUBTREE_SHORTCUT;
426                 else
427                         bi->sql_flags &= ~BSQLF_USE_SUBTREE_SHORTCUT;
428                 break;
429         case BSQL_FETCH_ALL_ATTRS:
430                 if ( c->value_int )
431                         bi->sql_flags |= BSQLF_FETCH_ALL_ATTRS;
432                 else
433                         bi->sql_flags &= ~BSQLF_FETCH_ALL_ATTRS;
434                 break;
435         case BSQL_CHECK_SCHEMA:
436                 if ( c->value_int )
437                         bi->sql_flags |= BSQLF_CHECK_SCHEMA;
438                 else
439                         bi->sql_flags &= ~BSQLF_CHECK_SCHEMA;
440                 break;
441         case BSQL_AUTOCOMMIT:
442                 if ( c->value_int )
443                         bi->sql_flags |= BSQLF_AUTOCOMMIT_ON;
444                 else
445                         bi->sql_flags &= ~BSQLF_AUTOCOMMIT_ON;
446                 break;
447         case BSQL_BASE_OBJECT:
448                 if ( c->be->be_nsuffix == NULL ) {
449                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
450                                 "%s: suffix must be set", c->log );
451                         Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
452                         rc = ARG_BAD_CONF;
453                         break;
454                 }
455                 if ( bi->sql_baseObject ) {
456                         Debug( LDAP_DEBUG_CONFIG,
457                                 "%s: "
458                                 "\"baseObject\" already provided (will be overwritten)\n",
459                                 c->log, 0, 0 );
460                         entry_free( bi->sql_baseObject );
461                 }
462                 if ( c->argc == 2 && !strcmp( c->argv[1], "TRUE" ))
463                         c->argc = 1;
464                 switch( c->argc ) {
465                 case 1:
466                         return create_baseObject( c->be, c->fname, c->lineno );
467
468                 case 2:
469                         rc = read_baseObject( c->be, c->argv[ 1 ] );
470                         if ( rc == 0 ) {
471                                 ch_free( bi->sql_base_ob_file );
472                                 bi->sql_base_ob_file = c->value_string;
473                         }
474                         return rc;
475
476                 default:
477                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
478                                 "%s: trailing values in directive", c->log );
479                         Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
480                         return 1;
481                 }
482                 break;
483         case BSQL_LAYER:
484                 if ( backsql_api_config( bi, c->argv[ 1 ], c->argc - 2, &c->argv[ 2 ] ) )
485                 {
486                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
487                                 "%s: unable to load sql layer", c->log );
488                         Debug( LDAP_DEBUG_ANY, "%s \"%s\"\n",
489                                 c->cr_msg, c->argv[1], 0 );
490                         return 1;
491                 }
492                 break;
493         case BSQL_ALIASING_KEYWORD:
494                 if ( ! BER_BVISNULL( &bi->sql_aliasing ) ) {
495                         ch_free( bi->sql_aliasing.bv_val );
496                 }
497
498                 ber_str2bv( c->argv[ 1 ], strlen( c->argv[ 1 ] ) + 1, 1,
499                         &bi->sql_aliasing );
500                 /* add a trailing space... */
501                 bi->sql_aliasing.bv_val[ bi->sql_aliasing.bv_len - 1] = ' ';
502                 break;
503         case BSQL_FETCH_ATTRS: {
504                 char            *str, *s, *next;
505                 const char      *delimstr = ",";
506
507                 str = ch_strdup( c->argv[ 1 ] );
508                 for ( s = ldap_pvt_strtok( str, delimstr, &next );
509                                 s != NULL;
510                                 s = ldap_pvt_strtok( NULL, delimstr, &next ) )
511                 {
512                         if ( strlen( s ) == 1 ) {
513                                 if ( *s == '*' ) {
514                                         bi->sql_flags |= BSQLF_FETCH_ALL_USERATTRS;
515                                         c->argv[ 1 ][ s - str ] = ',';
516
517                                 } else if ( *s == '+' ) {
518                                         bi->sql_flags |= BSQLF_FETCH_ALL_OPATTRS;
519                                         c->argv[ 1 ][ s - str ] = ',';
520                                 }
521                         }
522                 }
523                 ch_free( str );
524                 bi->sql_anlist = str2anlist( bi->sql_anlist, c->argv[ 1 ], delimstr );
525                 if ( bi->sql_anlist == NULL ) {
526                         return -1;
527                 }
528                 }
529                 break;
530         }
531         return rc;
532 }
533
534 /*
535  * Read the entries specified in fname and merge the attributes
536  * to the user defined baseObject entry. Note that if we find any errors
537  * what so ever, we will discard the entire entries, print an
538  * error message and return.
539  */
540 static int
541 read_baseObject( 
542         BackendDB       *be,
543         const char      *fname )
544 {
545         backsql_info    *bi = (backsql_info *)be->be_private;
546         LDIFFP          *fp;
547         int             rc = 0, lmax = 0, ldifrc;
548         unsigned long   lineno = 0;
549         char            *buf = NULL;
550
551         assert( fname != NULL );
552
553         fp = ldif_open( fname, "r" );
554         if ( fp == NULL ) {
555                 Debug( LDAP_DEBUG_ANY,
556                         "could not open back-sql baseObject "
557                         "attr file \"%s\" - absolute path?\n",
558                         fname, 0, 0 );
559                 perror( fname );
560                 return LDAP_OTHER;
561         }
562
563         bi->sql_baseObject = entry_alloc();
564         if ( bi->sql_baseObject == NULL ) {
565                 Debug( LDAP_DEBUG_ANY,
566                         "read_baseObject_file: entry_alloc failed", 0, 0, 0 );
567                 ldif_close( fp );
568                 return LDAP_NO_MEMORY;
569         }
570         bi->sql_baseObject->e_name = be->be_suffix[0];
571         bi->sql_baseObject->e_nname = be->be_nsuffix[0];
572         bi->sql_baseObject->e_attrs = NULL;
573
574         while (( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
575                 Entry           *e = str2entry( buf );
576                 Attribute       *a;
577
578                 if( e == NULL ) {
579                         fprintf( stderr, "back-sql baseObject: "
580                                         "could not parse entry (line=%lu)\n",
581                                         lineno );
582                         rc = LDAP_OTHER;
583                         break;
584                 }
585
586                 /* make sure the DN is the database's suffix */
587                 if ( !be_issuffix( be, &e->e_nname ) ) {
588                         fprintf( stderr,
589                                 "back-sql: invalid baseObject - "
590                                 "dn=\"%s\" (line=%lu)\n",
591                                 e->e_name.bv_val, lineno );
592                         entry_free( e );
593                         rc = LDAP_OTHER;
594                         break;
595                 }
596
597                 /*
598                  * we found a valid entry, so walk thru all the attributes in the
599                  * entry, and add each attribute type and description to baseObject
600                  */
601                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
602                         if ( attr_merge( bi->sql_baseObject, a->a_desc,
603                                                 a->a_vals,
604                                                 ( a->a_nvals == a->a_vals ) ?
605                                                 NULL : a->a_nvals ) )
606                         {
607                                 rc = LDAP_OTHER;
608                                 break;
609                         }
610                 }
611
612                 entry_free( e );
613                 if ( rc ) {
614                         break;
615                 }
616         }
617
618         if ( ldifrc < 0 )
619                 rc = LDAP_OTHER;
620
621         if ( rc ) {
622                 entry_free( bi->sql_baseObject );
623                 bi->sql_baseObject = NULL;
624         }
625
626         ch_free( buf );
627
628         ldif_close( fp );
629
630         Debug( LDAP_DEBUG_CONFIG, "back-sql baseObject file \"%s\" read.\n",
631                         fname, 0, 0 );
632
633         return rc;
634 }
635
636 static int
637 create_baseObject(
638         BackendDB       *be,
639         const char      *fname,
640         int             lineno )
641 {
642         backsql_info    *bi = (backsql_info *)be->be_private;
643         LDAPRDN         rdn;
644         char            *p;
645         int             rc, iAVA;
646         char            buf[1024];
647
648         snprintf( buf, sizeof(buf),
649                         "dn: %s\n"
650                         "objectClass: extensibleObject\n"
651                         "description: builtin baseObject for back-sql\n"
652                         "description: all entries mapped "
653                                 "in table \"ldap_entries\" "
654                                 "must have "
655                                 "\"" BACKSQL_BASEOBJECT_IDSTR "\" "
656                                 "in the \"parent\" column",
657                         be->be_suffix[0].bv_val );
658
659         bi->sql_baseObject = str2entry( buf );
660         if ( bi->sql_baseObject == NULL ) {
661                 Debug( LDAP_DEBUG_TRACE,
662                         "<==backsql_db_config (%s line %d): "
663                         "unable to parse baseObject entry\n",
664                         fname, lineno, 0 );
665                 return 1;
666         }
667
668         if ( BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ) {
669                 return 0;
670         }
671
672         rc = ldap_bv2rdn( &be->be_suffix[ 0 ], &rdn, (char **)&p,
673                         LDAP_DN_FORMAT_LDAP );
674         if ( rc != LDAP_SUCCESS ) {
675                 snprintf( buf, sizeof(buf),
676                         "unable to extract RDN "
677                         "from baseObject DN \"%s\" (%d: %s)",
678                         be->be_suffix[ 0 ].bv_val,
679                         rc, ldap_err2string( rc ) );
680                 Debug( LDAP_DEBUG_TRACE,
681                         "<==backsql_db_config (%s line %d): %s\n",
682                         fname, lineno, buf );
683                 return 1;
684         }
685
686         for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
687                 LDAPAVA                         *ava = rdn[ iAVA ];
688                 AttributeDescription            *ad = NULL;
689                 slap_syntax_transform_func      *transf = NULL;
690                 struct berval                   bv = BER_BVNULL;
691                 const char                      *text = NULL;
692
693                 assert( ava != NULL );
694
695                 rc = slap_bv2ad( &ava->la_attr, &ad, &text );
696                 if ( rc != LDAP_SUCCESS ) {
697                         snprintf( buf, sizeof(buf),
698                                 "AttributeDescription of naming "
699                                 "attribute #%d from baseObject "
700                                 "DN \"%s\": %d: %s",
701                                 iAVA, be->be_suffix[ 0 ].bv_val,
702                                 rc, ldap_err2string( rc ) );
703                         Debug( LDAP_DEBUG_TRACE,
704                                 "<==backsql_db_config (%s line %d): %s\n",
705                                 fname, lineno, buf );
706                         return 1;
707                 }
708                 
709                 transf = ad->ad_type->sat_syntax->ssyn_pretty;
710                 if ( transf ) {
711                         /*
712                          * transform value by pretty function
713                          *      if value is empty, use empty_bv
714                          */
715                         rc = ( *transf )( ad->ad_type->sat_syntax,
716                                 ava->la_value.bv_len
717                                         ? &ava->la_value
718                                         : (struct berval *) &slap_empty_bv,
719                                 &bv, NULL );
720         
721                         if ( rc != LDAP_SUCCESS ) {
722                                 snprintf( buf, sizeof(buf),
723                                         "prettying of attribute #%d "
724                                         "from baseObject "
725                                         "DN \"%s\" failed: %d: %s",
726                                         iAVA, be->be_suffix[ 0 ].bv_val,
727                                         rc, ldap_err2string( rc ) );
728                                 Debug( LDAP_DEBUG_TRACE,
729                                         "<==backsql_db_config (%s line %d): "
730                                         "%s\n",
731                                         fname, lineno, buf );
732                                 return 1;
733                         }
734                 }
735
736                 if ( !BER_BVISNULL( &bv ) ) {
737                         if ( ava->la_flags & LDAP_AVA_FREE_VALUE ) {
738                                 ber_memfree( ava->la_value.bv_val );
739                         }
740                         ava->la_value = bv;
741                         ava->la_flags |= LDAP_AVA_FREE_VALUE;
742                 }
743
744                 attr_merge_normalize_one( bi->sql_baseObject,
745                                 ad, &ava->la_value, NULL );
746         }
747
748         ldap_rdnfree( rdn );
749
750         return 0;
751 }
752
753 int backsql_init_cf( BackendInfo *bi )
754 {
755         int rc;
756
757         bi->bi_cf_ocs = sqlocs;
758         rc = config_register_schema( sqlcfg, sqlocs );
759         if ( rc ) return rc;
760         return 0;
761 }