]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/config.c
1c596de1339fd83ba0e2282506664b80300cfa1c
[openldap] / servers / slapd / back-meta / config.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "slap.h"
31 #include "lutil.h"
32 #include "../back-ldap/back-ldap.h"
33 #undef ldap_debug       /* silence a warning in ldap-int.h */
34 #include "../../../libraries/libldap/ldap-int.h"
35 #include "back-meta.h"
36
37 static int
38 new_target( 
39         metatarget_t    *mt )
40 {
41         struct ldapmapping      *mapping;
42         char                    *rargv[ 3 ];
43
44         memset( mt, 0, sizeof( metatarget_t ) );
45
46         mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
47         if ( mt->mt_rwmap.rwm_rw == NULL ) {
48                 return -1;
49         }
50
51
52         /*
53          * the filter rewrite as a string must be disabled
54          * by default; it can be re-enabled by adding rules;
55          * this creates an empty rewriteContext
56          */
57         rargv[ 0 ] = "rewriteContext";
58         rargv[ 1 ] = "searchFilter";
59         rargv[ 2 ] = NULL;
60         rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
61
62         rargv[ 0 ] = "rewriteContext";
63         rargv[ 1 ] = "default";
64         rargv[ 2 ] = NULL;
65         rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
66
67         ldap_back_map_init( &mt->mt_rwmap.rwm_at, &mapping );
68
69         return 0;
70 }
71
72 static int
73 check_true_false( char *str )
74 {
75         if ( strcasecmp( str, "true" ) == 0 || strcasecmp( str, "yes" ) == 0 ) {
76                 return 1;
77         }
78
79         if ( strcasecmp( str, "false" ) == 0 || strcasecmp( str, "no" ) == 0 ) {
80                 return 0;
81         }
82
83         return -1;
84 }
85
86
87 int
88 meta_back_db_config(
89                 BackendDB       *be,
90                 const char      *fname,
91                 int             lineno,
92                 int             argc,
93                 char            **argv
94 )
95 {
96         metainfo_t      *mi = ( metainfo_t * )be->be_private;
97
98         if ( mi == NULL ) {
99                 fprintf( stderr, 
100         "%s: line %d: meta backend info is null!\n",
101                     fname, lineno );
102                 return 1;
103         }
104
105         /* URI of server to query */
106         if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
107                 int             i = mi->mi_ntargets;
108 #if 0
109                 int             j;
110 #endif /* uncomment if uri MUST be a branch of suffix */
111                 LDAPURLDesc     *ludp, *tmpludp;
112                 struct berval   dn;
113                 int             rc;
114                 int             c;
115                 
116                 if ( argc != 2 ) {
117                         fprintf( stderr,
118         "%s: line %d: missing address"
119         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
120                                 fname, lineno );
121                         return 1;
122                 }
123                 
124                 ++mi->mi_ntargets;
125
126                 mi->mi_targets = ( metatarget_t * )ch_realloc( mi->mi_targets, 
127                         sizeof( metatarget_t ) * mi->mi_ntargets );
128                 if ( mi->mi_targets == NULL ) {
129                         fprintf( stderr,
130         "%s: line %d: out of memory while storing server name"
131         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
132                                 fname, lineno );
133                         return 1;
134                 }
135
136                 if ( new_target( &mi->mi_targets[ i ] ) != 0 ) {
137                         fprintf( stderr,
138         "%s: line %d: unable to init server"
139         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
140                                 fname, lineno );
141                         return 1;
142                 }
143
144                 mi->mi_targets[ i ].mt_nretries = mi->mi_nretries;
145                 mi->mi_targets[ i ].mt_flags = mi->flags;
146                 mi->mi_targets[ i ].mt_version = mi->mi_version;
147
148                 for ( c = 0; c < META_OP_LAST; c++ ) {
149                         mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ];
150                 }
151
152                 /*
153                  * uri MUST be legal!
154                  */
155                 if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) {
156                         fprintf( stderr,
157         "%s: line %d: unable to parse URI"
158         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
159                                 fname, lineno );
160                         return 1;
161                 }
162
163                 /*
164                  * uri MUST have the <dn> part!
165                  */
166                 if ( ludp->lud_dn == NULL || ludp->lud_dn[ 0 ] == '\0' ) {
167                         fprintf( stderr,
168         "%s: line %d: missing <naming context> "
169         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
170                                 fname, lineno );
171                         return 1;
172                 }
173
174                 /*
175                  * copies and stores uri and suffix
176                  */
177                 ber_str2bv( ludp->lud_dn, 0, 0, &dn );
178                 rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix,
179                         &mi->mi_targets[ i ].mt_nsuffix, NULL );
180                 if( rc != LDAP_SUCCESS ) {
181                         fprintf( stderr, "%s: line %d: "
182                                         "target '%s' DN is invalid\n",
183                                         fname, lineno, argv[ 1 ] );
184                         return( 1 );
185                 }
186
187                 ludp->lud_dn[ 0 ] = '\0';
188
189                 switch ( ludp->lud_scope ) {
190                 case LDAP_SCOPE_DEFAULT:
191                         mi->mi_targets[ i ].mt_scope = LDAP_SCOPE_SUBTREE;
192                         break;
193
194                 case LDAP_SCOPE_SUBTREE:
195 #ifdef LDAP_SCOPE_SUBORDINATE
196                 case LDAP_SCOPE_SUBORDINATE:
197 #endif /* LDAP_SCOPE_SUBORDINATE */
198                         mi->mi_targets[ i ].mt_scope = ludp->lud_scope;
199                         break;
200
201                 default:
202                         fprintf( stderr, "%s: line %d: "
203                                         "invalid scope for target '%s'\n",
204                                         fname, lineno, argv[ 1 ] );
205                         return( 1 );
206                 }
207
208                 /* check all, to apply the scope check on the first one */
209                 for ( tmpludp = ludp; tmpludp; tmpludp = tmpludp->lud_next ) {
210                         if ( tmpludp->lud_dn != NULL && tmpludp->lud_dn[ 0 ] != '\0' ) {
211                                 fprintf( stderr, "%s: line %d: "
212                                                 "multiple URIs must have "
213                                                 "no DN part\n",
214                                         fname, lineno );
215                                 return( 1 );
216
217                         }
218                 }
219
220                 mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp );
221                 ldap_free_urllist( ludp );
222                 if ( mi->mi_targets[ i ].mt_uri == NULL) {
223                         fprintf( stderr, "%s: line %d: no memory?\n",
224                                         fname, lineno );
225                         return( 1 );
226                 }
227                 
228                 /*
229                  * uri MUST be a branch of suffix!
230                  */
231 #if 0 /* too strict a constraint */
232                 if ( select_backend( &mi->mi_targets[ i ].suffix, 0, 0 ) != be ) {
233                         fprintf( stderr,
234         "%s: line %d: <naming context> of URI does not refer to current backend"
235         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
236                                 fname, lineno );
237                         return 1;
238                 }
239 #else
240                 /*
241                  * uri MUST be a branch of a suffix!
242                  */
243                 if ( select_backend( &mi->mi_targets[ i ].mt_nsuffix, 0, 0 ) == NULL ) {
244                         fprintf( stderr,
245         "%s: line %d: <naming context> of URI does not resolve to a backend"
246         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
247                                 fname, lineno );
248                         return 1;
249                 }
250 #endif
251
252         /* default target directive */
253         } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
254                 int             i = mi->mi_ntargets - 1;
255                 
256                 if ( argc == 1 ) {
257                         if ( i < 0 ) {
258                                 fprintf( stderr,
259         "%s: line %d: \"default-target\" alone need be"
260         " inside a \"uri\" directive\n",
261                                         fname, lineno );
262                                 return 1;
263                         }
264                         mi->mi_defaulttarget = i;
265                 } else {
266                         if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) {
267                                 if ( i >= 0 ) {
268                                         fprintf( stderr,
269         "%s: line %d: \"default-target none\""
270         " should go before uri definitions\n",
271                                                 fname, lineno );
272                                 }
273                                 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
274
275                         } else {
276                                 char    *next;
277                                 int     n = strtol( argv[ 1 ], &next, 10 );
278                                 if ( n < 0 || n >= i - 1 ) {
279                                         fprintf( stderr,
280         "%s: line %d: illegal target number %d\n",
281                                                 fname, lineno, n );
282                                         return 1;
283                                 }
284                                 mi->mi_defaulttarget = n;
285                         }
286                 }
287                 
288         /* ttl of dn cache */
289         } else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) {
290                 if ( argc != 2 ) {
291                         fprintf( stderr,
292         "%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n",
293                                 fname, lineno );
294                         return 1;
295                 }
296                 
297                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
298                         mi->mi_cache.ttl = META_DNCACHE_FOREVER;
299                 } else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) {
300                         mi->mi_cache.ttl = META_DNCACHE_DISABLED;
301                 } else {
302                         mi->mi_cache.ttl = atol( argv[ 1 ] );
303                 }
304
305         /* network timeout when connecting to ldap servers */
306         } else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) {
307                 if ( argc != 2 ) {
308                         fprintf( stderr,
309         "%s: line %d: missing network timeout in \"network-timeout <seconds>\" line\n",
310                                 fname, lineno );
311                         return 1;
312                 }
313                 mi->mi_network_timeout = atol(argv[ 1 ]);
314
315         /* name to use for meta_back_group */
316         } else if ( strcasecmp( argv[ 0 ], "acl-authcDN" ) == 0
317                         || strcasecmp( argv[ 0 ], "binddn" ) == 0 )
318         {
319                 int             i = mi->mi_ntargets - 1;
320                 struct berval   dn;
321
322                 if ( i < 0 ) {
323                         fprintf( stderr,
324         "%s: line %d: need \"uri\" directive first\n",
325                                 fname, lineno );
326                         return 1;
327                 }
328                 
329                 if ( argc != 2 ) {
330                         fprintf( stderr,
331         "%s: line %d: missing name in \"binddn <name>\" line\n",
332                                 fname, lineno );
333                         return 1;
334                 }
335
336                 if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) {
337                         fprintf( stderr, "%s: line %d: "
338                                 "\"binddn\" statement is deprecated; "
339                                 "use \"acl-authcDN\" instead\n",
340                                 fname, lineno );
341                         /* FIXME: some day we'll need to throw an error */
342                 }
343
344                 dn.bv_val = argv[ 1 ];
345                 dn.bv_len = strlen( argv[ 1 ] );
346                 if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ].mt_binddn,
347                         NULL ) != LDAP_SUCCESS )
348                 {
349                         fprintf( stderr, "%s: line %d: "
350                                         "bind DN '%s' is invalid\n",
351                                         fname, lineno, argv[ 1 ] );
352                         return( 1 );
353                 }
354
355         /* password to use for meta_back_group */
356         } else if ( strcasecmp( argv[ 0 ], "acl-passwd" ) == 0
357                         || strcasecmp( argv[ 0 ], "bindpw" ) == 0 )
358         {
359                 int             i = mi->mi_ntargets - 1;
360
361                 if ( i < 0 ) {
362                         fprintf( stderr,
363         "%s: line %d: need \"uri\" directive first\n",
364                                 fname, lineno );
365                         return 1;
366                 }
367                 
368                 if ( argc != 2 ) {
369                         fprintf( stderr,
370         "%s: line %d: missing password in \"bindpw <password>\" line\n",
371                             fname, lineno );
372                         return 1;
373                 }
374
375                 if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) {
376                         fprintf( stderr, "%s: line %d: "
377                                 "\"bindpw\" statement is deprecated; "
378                                 "use \"acl-passwd\" instead\n",
379                                 fname, lineno );
380                         /* FIXME: some day we'll need to throw an error */
381                 }
382
383                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_bindpw );
384                 
385         /* save bind creds for referral rebinds? */
386         } else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) {
387                 if ( argc > 2 ) {
388                         fprintf( stderr,
389         "%s: line %d: \"rebind-as-user {NO|yes}\" takes 1 argument.\n",
390                             fname, lineno );
391                         return( 1 );
392                 }
393
394                 if ( argc == 1 ) {
395                         fprintf( stderr,
396         "%s: line %d: deprecated use of \"rebind-as-user {FALSE|true}\" with no arguments.\n",
397                             fname, lineno );
398                         mi->flags |= LDAP_BACK_F_SAVECRED;
399
400                 } else {
401                         switch ( check_true_false( argv[ 1 ] ) ) {
402                         case 0:
403                                 mi->flags &= ~LDAP_BACK_F_SAVECRED;
404                                 break;
405
406                         case 1:
407                                 mi->flags |= LDAP_BACK_F_SAVECRED;
408                                 break;
409
410                         default:
411                                 fprintf( stderr,
412         "%s: line %d: \"rebind-as-user {FALSE|true}\" unknown argument \"%s\".\n",
413                                     fname, lineno, argv[ 1 ] );
414                                 return 1;
415                         }
416                 }
417
418         } else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) {
419                 unsigned        *flagsp = mi->mi_ntargets ?
420                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
421                                 : &mi->flags;
422
423                 if ( argc != 2 ) {
424                         fprintf( stderr,
425         "%s: line %d: \"chase-referrals {TRUE|false}\" needs 1 argument.\n",
426                                         fname, lineno );
427                         return( 1 );
428                 }
429
430                 /* this is the default; we add it because the default might change... */
431                 switch ( check_true_false( argv[ 1 ] ) ) {
432                 case 1:
433                         *flagsp |= LDAP_BACK_F_CHASE_REFERRALS;
434                         break;
435
436                 case 0:
437                         *flagsp &= ~LDAP_BACK_F_CHASE_REFERRALS;
438                         break;
439
440                 default:
441                         fprintf( stderr,
442                 "%s: line %d: \"chase-referrals {TRUE|false}\": unknown argument \"%s\".\n",
443                                         fname, lineno, argv[ 1 ] );
444                         return( 1 );
445                 }
446         
447         } else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) {
448                 unsigned        *flagsp = mi->mi_ntargets ?
449                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
450                                 : &mi->flags;
451
452                 if ( argc != 2 ) {
453                         fprintf( stderr,
454                 "%s: line %d: \"tls <what>\" needs 1 argument.\n",
455                                         fname, lineno );
456                         return( 1 );
457                 }
458
459                 /* start */
460                 if ( strcasecmp( argv[ 1 ], "start" ) == 0 ) {
461                         *flagsp |= ( LDAP_BACK_F_USE_TLS | LDAP_BACK_F_TLS_CRITICAL );
462         
463                 /* try start tls */
464                 } else if ( strcasecmp( argv[ 1 ], "try-start" ) == 0 ) {
465                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
466                         *flagsp |= LDAP_BACK_F_USE_TLS;
467         
468                 /* propagate start tls */
469                 } else if ( strcasecmp( argv[ 1 ], "propagate" ) == 0 ) {
470                         *flagsp |= ( LDAP_BACK_F_PROPAGATE_TLS | LDAP_BACK_F_TLS_CRITICAL );
471                 
472                 /* try start tls */
473                 } else if ( strcasecmp( argv[ 1 ], "try-propagate" ) == 0 ) {
474                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
475                         *flagsp |= LDAP_BACK_F_PROPAGATE_TLS;
476
477                 } else {
478                         fprintf( stderr,
479                 "%s: line %d: \"tls <what>\": unknown argument \"%s\".\n",
480                                         fname, lineno, argv[ 1 ] );
481                         return( 1 );
482                 }
483
484         } else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) {
485                 unsigned        *flagsp = mi->mi_ntargets ?
486                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
487                                 : &mi->flags;
488
489                 if ( argc != 2 ) {
490                         fprintf( stderr,
491                 "%s: line %d: \"t-f-support {FALSE|true|discover}\" needs 1 argument.\n",
492                                         fname, lineno );
493                         return( 1 );
494                 }
495
496                 switch ( check_true_false( argv[ 1 ] ) ) {
497                 case 0:
498                         *flagsp &= ~(LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER);
499                         break;
500
501                 case 1:
502                         *flagsp |= LDAP_BACK_F_SUPPORT_T_F;
503                         break;
504
505                 default:
506                         if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
507                                 *flagsp |= LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
508
509                         } else {
510                                 fprintf( stderr,
511         "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
512                                         fname, lineno, argv[ 1 ] );
513                                 return 1;
514                         }
515                         break;
516                 }
517
518         /* onerr? */
519         } else if ( strcasecmp( argv[ 0 ], "onerr" ) == 0 ) {
520                 if ( argc != 2 ) {
521                         fprintf( stderr,
522         "%s: line %d: \"onerr {CONTINUE|stop}\" takes 1 argument\n",
523                             fname, lineno );
524                         return( 1 );
525                 }
526
527                 if ( strcasecmp( argv[ 1 ], "continue" ) == 0 ) {
528                         mi->flags &= ~META_BACK_F_ONERR_STOP;
529
530                 } else if ( strcasecmp( argv[ 1 ], "stop" ) == 0 ) {
531                         mi->flags |= META_BACK_F_ONERR_STOP;
532
533                 } else {
534                         fprintf( stderr,
535         "%s: line %d: \"onerr {CONTINUE|stop}\": invalid arg \"%s\".\n",
536                                 fname, lineno, argv[ 1 ] );
537                         return 1;
538                 }
539
540         /* bind-defer? */
541         } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0 ) {
542                 if ( argc != 2 ) {
543                         fprintf( stderr,
544         "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\" takes 1 argument\n",
545                             fname, lineno );
546                         return( 1 );
547                 }
548
549                 switch ( check_true_false( argv[ 1 ] ) ) {
550                 case 0:
551                         mi->flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
552                         break;
553
554                 case 1:
555                         mi->flags |= META_BACK_F_DEFER_ROOTDN_BIND;
556                         break;
557
558                 default:
559                         fprintf( stderr,
560         "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\": invalid arg \"%s\".\n",
561                                 fname, lineno, argv[ 1 ] );
562                         return 1;
563                 }
564
565         } else if ( strcasecmp( argv[ 0 ], "timeout" ) == 0 ) {
566                 char    *sep, *next;
567                 time_t  *tv = mi->mi_ntargets ?
568                                 mi->mi_targets[ mi->mi_ntargets - 1 ].mt_timeout
569                                 : mi->mi_timeout;
570                 int     c;
571
572                 if ( argc < 2 ) {
573                         fprintf( stderr,
574         "%s: line %d: \"timeout [{add|delete|modify|modrdn}=]<val> [...]\" takes at least 1 argument\n",
575                             fname, lineno );
576                         return( 1 );
577                 }
578
579                 for ( c = 1; c < argc; c++ ) {
580                         time_t  *t = NULL, val;
581
582                         sep = strchr( argv[ c ], '=' );
583                         if ( sep != NULL ) {
584                                 size_t  len = sep - argv[ c ];
585
586                                 if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
587                                         t = &tv[ META_OP_ADD ];
588                                 } else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
589                                         t = &tv[ META_OP_DELETE ];
590                                 } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
591                                         t = &tv[ META_OP_MODIFY ];
592                                 } else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
593                                         t = &tv[ META_OP_MODRDN ];
594                                 } else {
595                                         fprintf( stderr,
596                 "%s: line %d: unknown operation \"%s\" for timeout #%d.\n",
597                                                 fname, lineno, argv[ c ], c );
598                                         return 1;
599                                 }
600                                 sep++;
601         
602                         } else {
603                                 sep = argv[ c ];
604                         }
605         
606                         val = strtoul( sep, &next, 10 );
607                         if ( next == sep || next[ 0 ] != '\0' ) {
608                                 fprintf( stderr,
609                 "%s: line %d: unable to parse value \"%s\" for timeout.\n",
610                                         fname, lineno, sep );
611                                 return 1;
612                         }
613                 
614                         if ( t ) {
615                                 *t = val;
616         
617                         } else {
618                                 int     i;
619         
620                                 for ( i = 0; i < META_OP_LAST; i++ ) {
621                                         tv[ i ] = val;
622                                 }
623                         }
624                 }
625         
626         /* name to use as pseudo-root dn */
627         } else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) {
628                 int             i = mi->mi_ntargets - 1;
629                 struct berval   dn;
630
631                 if ( i < 0 ) {
632                         fprintf( stderr,
633         "%s: line %d: need \"uri\" directive first\n",
634                                 fname, lineno );
635                         return 1;
636                 }
637                 
638                 if ( argc != 2 ) {
639                         fprintf( stderr,
640         "%s: line %d: missing name in \"pseudorootdn <name>\" line\n",
641                                 fname, lineno );
642                         return 1;
643                 }
644
645                 dn.bv_val = argv[ 1 ];
646                 dn.bv_len = strlen( argv[ 1 ] );
647                 if ( dnNormalize( 0, NULL, NULL, &dn,
648                         &mi->mi_targets[ i ].mt_pseudorootdn, NULL ) != LDAP_SUCCESS )
649                 {
650                         fprintf( stderr, "%s: line %d: "
651                                         "pseudoroot DN '%s' is invalid\n",
652                                         fname, lineno, argv[ 1 ] );
653                         return( 1 );
654                 }
655
656         /* password to use as pseudo-root */
657         } else if ( strcasecmp( argv[ 0 ], "pseudorootpw" ) == 0 ) {
658                 int             i = mi->mi_ntargets - 1;
659
660                 if ( i < 0 ) {
661                         fprintf( stderr,
662         "%s: line %d: need \"uri\" directive first\n",
663                                 fname, lineno );
664                         return 1;
665                 }
666                 
667                 if ( argc != 2 ) {
668                         fprintf( stderr,
669         "%s: line %d: missing password in \"pseudorootpw <password>\" line\n",
670                             fname, lineno );
671                         return 1;
672                 }
673                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_pseudorootpw );
674         
675         /* dn massaging */
676         } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
677                 BackendDB       *tmp_be;
678                 int             i = mi->mi_ntargets - 1, rc;
679                 struct berval   dn, nvnc, pvnc, nrnc, prnc;
680
681                 if ( i < 0 ) {
682                         fprintf( stderr,
683         "%s: line %d: need \"uri\" directive first\n",
684                                 fname, lineno );
685                         return 1;
686                 }
687                 
688                 /*
689                  * syntax:
690                  * 
691                  *      suffixmassage <suffix> <massaged suffix>
692                  *
693                  * the <suffix> field must be defined as a valid suffix
694                  * (or suffixAlias?) for the current database;
695                  * the <massaged suffix> shouldn't have already been
696                  * defined as a valid suffix or suffixAlias for the 
697                  * current server
698                  */
699                 if ( argc != 3 ) {
700                         fprintf( stderr,
701         "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
702                                 fname, lineno );
703                         return 1;
704                 }
705
706                 ber_str2bv( argv[ 1 ], 0, 0, &dn );
707                 if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
708                         fprintf( stderr, "%s: line %d: "
709                                         "suffix '%s' is invalid\n",
710                                         fname, lineno, argv[ 1 ] );
711                         return 1;
712                 }
713                 
714                 tmp_be = select_backend( &nvnc, 0, 0 );
715                 if ( tmp_be != NULL && tmp_be != be ) {
716                         fprintf( stderr, 
717         "%s: line %d: suffix already in use by another backend in"
718         " \"suffixMassage <suffix> <massaged suffix>\"\n",
719                                 fname, lineno );
720                         free( pvnc.bv_val );
721                         free( nvnc.bv_val );
722                         return 1;                                               
723                 }
724
725                 ber_str2bv( argv[ 2 ], 0, 0, &dn );
726                 if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
727                         fprintf( stderr, "%s: line %d: "
728                                         "massaged suffix '%s' is invalid\n",
729                                         fname, lineno, argv[ 2 ] );
730                         free( pvnc.bv_val );
731                         free( nvnc.bv_val );
732                         return 1;
733                 }
734         
735 #if 0   
736                 tmp_be = select_backend( &nrnc, 0, 0 );
737                 if ( tmp_be != NULL ) {
738                         fprintf( stderr,
739         "%s: line %d: massaged suffix already in use by another backend in" 
740         " \"suffixMassage <suffix> <massaged suffix>\"\n",
741                                 fname, lineno );
742                         free( pvnc.bv_val );
743                         free( nvnc.bv_val );
744                         free( prnc.bv_val );
745                         free( nrnc.bv_val );
746                         return 1;
747                 }
748 #endif
749                 
750                 /*
751                  * The suffix massaging is emulated by means of the
752                  * rewrite capabilities
753                  * FIXME: no extra rewrite capabilities should be added
754                  * to the database
755                  */
756                 rc = suffix_massage_config( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
757                                 &pvnc, &nvnc, &prnc, &nrnc );
758
759                 free( pvnc.bv_val );
760                 free( nvnc.bv_val );
761                 free( prnc.bv_val );
762                 free( nrnc.bv_val );
763
764                 return rc;
765                 
766         /* rewrite stuff ... */
767         } else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
768                 int             i = mi->mi_ntargets - 1;
769
770                 if ( i < 0 ) {
771                         fprintf( stderr, "%s: line %d: \"rewrite\" "
772                                 "statement outside target definition.\n",
773                                 fname, lineno );
774                         return 1;
775                 }
776                 
777                 return rewrite_parse( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
778                                 fname, lineno, argc, argv );
779
780         /* objectclass/attribute mapping */
781         } else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) {
782                 int             i = mi->mi_ntargets - 1;
783
784                 if ( i < 0 ) {
785                         fprintf( stderr,
786         "%s: line %d: need \"uri\" directive first\n",
787                                 fname, lineno );
788                         return 1;
789                 }
790
791                 return ldap_back_map_config( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, 
792                                 &mi->mi_targets[ i ].mt_rwmap.rwm_at,
793                                 fname, lineno, argc, argv );
794
795         } else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
796                 int             i = mi->mi_ntargets - 1;
797                 int             nretries = META_RETRY_UNDEFINED;
798
799                 if ( argc != 2 ) {
800                         fprintf( stderr,
801         "%s: line %d: need value in \"nretries <value>\"\n",
802                                 fname, lineno );
803                         return 1;
804                 }
805
806                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
807                         nretries = META_RETRY_FOREVER;
808
809                 } else if ( strcasecmp( argv[ 1 ], "never" ) == 0 ) {
810                         nretries = META_RETRY_NEVER;
811
812                 } else {
813                         char    *next;
814
815                         nretries = strtol( argv[ 1 ], &next, 10 );
816                         if ( next == argv[ 1 ] || next[ 0 ] != '\0' ) {
817                                 fprintf( stderr,
818         "%s: line %d: unable to parse value \"%s\" in \"nretries <value>\"\n",
819                                         fname, lineno, argv[ 1 ] );
820                                 return 1;
821                         }
822                 }
823
824                 if ( i < 0 ) {
825                         mi->mi_nretries = nretries;
826
827                 } else {
828                         mi->mi_targets[ i ].mt_nretries = nretries;
829                 }
830
831         /* anything else */
832         } else {
833                 return SLAP_CONF_UNKNOWN;
834         }
835         return 0;
836 }
837
838 int
839 ldap_back_map_config(
840                 struct ldapmap  *oc_map,
841                 struct ldapmap  *at_map,
842                 const char      *fname,
843                 int             lineno,
844                 int             argc,
845                 char            **argv )
846 {
847         struct ldapmap          *map;
848         struct ldapmapping      *mapping;
849         char                    *src, *dst;
850         int                     is_oc = 0;
851
852         if ( argc < 3 || argc > 4 ) {
853                 fprintf( stderr,
854         "%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
855                         fname, lineno );
856                 return 1;
857         }
858
859         if ( strcasecmp( argv[ 1 ], "objectclass" ) == 0 ) {
860                 map = oc_map;
861                 is_oc = 1;
862
863         } else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) {
864                 map = at_map;
865
866         } else {
867                 fprintf( stderr, "%s: line %d: syntax is "
868                         "\"map {objectclass | attribute} [<local> | *] "
869                         "{<foreign> | *}\"\n",
870                         fname, lineno );
871                 return 1;
872         }
873
874         if ( strcmp( argv[ 2 ], "*" ) == 0 ) {
875                 if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) {
876                         map->drop_missing = ( argc < 4 );
877                         return 0;
878                 }
879                 src = dst = argv[ 3 ];
880
881         } else if ( argc < 4 ) {
882                 src = "";
883                 dst = argv[ 2 ];
884
885         } else {
886                 src = argv[ 2 ];
887                 dst = ( strcmp( argv[ 3 ], "*" ) == 0 ? src : argv[ 3 ] );
888         }
889
890         if ( ( map == at_map )
891                         && ( strcasecmp( src, "objectclass" ) == 0
892                         || strcasecmp( dst, "objectclass" ) == 0 ) )
893         {
894                 fprintf( stderr,
895                         "%s: line %d: objectclass attribute cannot be mapped\n",
896                         fname, lineno );
897         }
898
899         mapping = (struct ldapmapping *)ch_calloc( 2,
900                 sizeof(struct ldapmapping) );
901         if ( mapping == NULL ) {
902                 fprintf( stderr,
903                         "%s: line %d: out of memory\n",
904                         fname, lineno );
905                 return 1;
906         }
907         ber_str2bv( src, 0, 1, &mapping->src );
908         ber_str2bv( dst, 0, 1, &mapping->dst );
909         mapping[ 1 ].src = mapping->dst;
910         mapping[ 1 ].dst = mapping->src;
911
912         /*
913          * schema check
914          */
915         if ( is_oc ) {
916                 if ( src[ 0 ] != '\0' ) {
917                         if ( oc_bvfind( &mapping->src ) == NULL ) {
918                                 fprintf( stderr,
919         "%s: line %d: warning, source objectClass '%s' "
920         "should be defined in schema\n",
921                                         fname, lineno, src );
922
923                                 /*
924                                  * FIXME: this should become an err
925                                  */
926                                 goto error_return;
927                         }
928                 }
929
930                 if ( oc_bvfind( &mapping->dst ) == NULL ) {
931                         fprintf( stderr,
932         "%s: line %d: warning, destination objectClass '%s' "
933         "is not defined in schema\n",
934                                 fname, lineno, dst );
935                 }
936         } else {
937                 int                     rc;
938                 const char              *text = NULL;
939                 AttributeDescription    *ad = NULL;
940
941                 if ( src[ 0 ] != '\0' ) {
942                         rc = slap_bv2ad( &mapping->src, &ad, &text );
943                         if ( rc != LDAP_SUCCESS ) {
944                                 fprintf( stderr,
945         "%s: line %d: warning, source attributeType '%s' "
946         "should be defined in schema\n",
947                                         fname, lineno, src );
948
949                                 /*
950                                  * FIXME: this should become an err
951                                  */
952                                 /*
953                                  * we create a fake "proxied" ad 
954                                  * and add it here.
955                                  */
956
957                                 rc = slap_bv2undef_ad( &mapping->src,
958                                                 &ad, &text, SLAP_AD_PROXIED );
959                                 if ( rc != LDAP_SUCCESS ) {
960                                         fprintf( stderr,
961         "%s: line %d: source attributeType '%s': %d (%s)\n",
962                                                 fname, lineno, src,
963                                                 rc, text ? text : "" );
964                                         goto error_return;
965                                 }
966                         }
967
968                         ad = NULL;
969                 }
970
971                 rc = slap_bv2ad( &mapping->dst, &ad, &text );
972                 if ( rc != LDAP_SUCCESS ) {
973                         fprintf( stderr,
974         "%s: line %d: warning, destination attributeType '%s' "
975         "is not defined in schema\n",
976                                 fname, lineno, dst );
977
978                         /*
979                          * we create a fake "proxied" ad 
980                          * and add it here.
981                          */
982
983                         rc = slap_bv2undef_ad( &mapping->dst,
984                                         &ad, &text, SLAP_AD_PROXIED );
985                         if ( rc != LDAP_SUCCESS ) {
986                                 fprintf( stderr,
987         "%s: line %d: source attributeType '%s': %d (%s)\n",
988                                         fname, lineno, dst,
989                                         rc, text ? text : "" );
990                                 return 1;
991                         }
992                 }
993         }
994
995         if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)mapping, mapping_cmp ) != NULL)
996                         || avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
997         {
998                 fprintf( stderr,
999                         "%s: line %d: duplicate mapping found" SLAPD_CONF_UNKNOWN_IGNORED ".\n",
1000                         fname, lineno );
1001                 goto error_return;
1002         }
1003
1004         if ( src[ 0 ] != '\0' ) {
1005                 avl_insert( &map->map, (caddr_t)mapping,
1006                                         mapping_cmp, mapping_dup );
1007         }
1008         avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
1009                                 mapping_cmp, mapping_dup );
1010
1011         return 0;
1012
1013 error_return:;
1014         if ( mapping ) {
1015                 ch_free( mapping->src.bv_val );
1016                 ch_free( mapping->dst.bv_val );
1017                 ch_free( mapping );
1018         }
1019
1020         return 1;
1021 }
1022
1023
1024 #ifdef ENABLE_REWRITE
1025 static char *
1026 suffix_massage_regexize( const char *s )
1027 {
1028         char *res, *ptr;
1029         const char *p, *r;
1030         int i;
1031
1032         for ( i = 0, p = s; 
1033                         ( r = strchr( p, ',' ) ) != NULL; 
1034                         p = r + 1, i++ )
1035                 ;
1036
1037         res = ch_calloc( sizeof( char ),
1038                         strlen( s )
1039                         + STRLENOF( "(.+,)?" )
1040                         + STRLENOF( "[ ]?" ) * i + 1 );
1041
1042         ptr = lutil_strcopy( res, "(.+,)?" );
1043         for ( i = 0, p = s;
1044                         ( r = strchr( p, ',' ) ) != NULL;
1045                         p = r + 1 , i++ ) {
1046                 ptr = lutil_strncopy( ptr, p, r - p + 1 );
1047                 ptr = lutil_strcopy( ptr, "[ ]?" );
1048
1049                 if ( r[ 1 ] == ' ' ) {
1050                         r++;
1051                 }
1052         }
1053         lutil_strcopy( ptr, p );
1054
1055         return res;
1056 }
1057
1058 static char *
1059 suffix_massage_patternize( const char *s )
1060 {
1061         ber_len_t       len;
1062         char            *res;
1063
1064         len = strlen( s );
1065
1066         res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
1067         if ( res == NULL ) {
1068                 return NULL;
1069         }
1070
1071         strcpy( res, "%1" );
1072         strcpy( &res[ STRLENOF( "%1" ) ], s );
1073
1074         return res;
1075 }
1076
1077 int
1078 suffix_massage_config( 
1079                 struct rewrite_info *info,
1080                 struct berval *pvnc,
1081                 struct berval *nvnc,
1082                 struct berval *prnc,
1083                 struct berval *nrnc
1084 )
1085 {
1086         char *rargv[ 5 ];
1087         int line = 0;
1088
1089         rargv[ 0 ] = "rewriteEngine";
1090         rargv[ 1 ] = "on";
1091         rargv[ 2 ] = NULL;
1092         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1093
1094         rargv[ 0 ] = "rewriteContext";
1095         rargv[ 1 ] = "default";
1096         rargv[ 2 ] = NULL;
1097         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1098
1099         rargv[ 0 ] = "rewriteRule";
1100         rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
1101         rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val );
1102         rargv[ 3 ] = ":";
1103         rargv[ 4 ] = NULL;
1104         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1105         ch_free( rargv[ 1 ] );
1106         ch_free( rargv[ 2 ] );
1107         
1108         rargv[ 0 ] = "rewriteContext";
1109         rargv[ 1 ] = "searchEntryDN";
1110         rargv[ 2 ] = NULL;
1111         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1112
1113         rargv[ 0 ] = "rewriteRule";
1114         rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
1115         rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val );
1116         rargv[ 3 ] = ":";
1117         rargv[ 4 ] = NULL;
1118         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1119         ch_free( rargv[ 1 ] );
1120         ch_free( rargv[ 2 ] );
1121
1122         /* backward compatibility */
1123         rargv[ 0 ] = "rewriteContext";
1124         rargv[ 1 ] = "searchResult";
1125         rargv[ 2 ] = "alias";
1126         rargv[ 3 ] = "searchEntryDN";
1127         rargv[ 4 ] = NULL;
1128         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1129         
1130         rargv[ 0 ] = "rewriteContext";
1131         rargv[ 1 ] = "matchedDN";
1132         rargv[ 2 ] = "alias";
1133         rargv[ 3 ] = "searchEntryDN";
1134         rargv[ 4 ] = NULL;
1135         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1136
1137         rargv[ 0 ] = "rewriteContext";
1138         rargv[ 1 ] = "searchAttrDN";
1139         rargv[ 2 ] = "alias";
1140         rargv[ 3 ] = "searchEntryDN";
1141         rargv[ 4 ] = NULL;
1142         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1143
1144         /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
1145          * see servers/slapd/overlays/rwm.h for details */
1146         rargv[ 0 ] = "rewriteContext";
1147         rargv[ 1 ] = "referralAttrDN";
1148         rargv[ 2 ] = NULL;
1149         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1150
1151         rargv[ 0 ] = "rewriteContext";
1152         rargv[ 1 ] = "referralDN";
1153         rargv[ 2 ] = NULL;
1154         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1155         
1156         return 0;
1157 }
1158 #endif /* ENABLE_REWRITE */
1159