]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/config.c
fix previous commit
[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 int
73 meta_back_db_config(
74                 BackendDB       *be,
75                 const char      *fname,
76                 int             lineno,
77                 int             argc,
78                 char            **argv
79 )
80 {
81         metainfo_t      *mi = ( metainfo_t * )be->be_private;
82
83         if ( mi == NULL ) {
84                 fprintf( stderr, 
85         "%s: line %d: meta backend info is null!\n",
86                     fname, lineno );
87                 return 1;
88         }
89
90         /* URI of server to query */
91         if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
92                 int             i = mi->mi_ntargets;
93 #if 0
94                 int             j;
95 #endif /* uncomment if uri MUST be a branch of suffix */
96                 LDAPURLDesc     *ludp, *tmpludp;
97                 struct berval   dn;
98                 int             rc;
99                 
100                 if ( argc != 2 ) {
101                         fprintf( stderr,
102         "%s: line %d: missing address"
103         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
104                                 fname, lineno );
105                         return 1;
106                 }
107                 
108                 ++mi->mi_ntargets;
109
110                 mi->mi_targets = ( metatarget_t * )ch_realloc( mi->mi_targets, 
111                         sizeof( metatarget_t ) * mi->mi_ntargets );
112                 if ( mi->mi_targets == NULL ) {
113                         fprintf( stderr,
114         "%s: line %d: out of memory while storing server name"
115         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
116                                 fname, lineno );
117                         return 1;
118                 }
119
120                 if ( new_target( &mi->mi_targets[ i ] ) != 0 ) {
121                         fprintf( stderr,
122         "%s: line %d: unable to init server"
123         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
124                                 fname, lineno );
125                         return 1;
126                 }
127
128                 mi->mi_targets[ i ].mt_nretries = mi->mi_nretries;
129                 mi->mi_targets[ i ].mt_flags = mi->flags;
130                 mi->mi_targets[ i ].mt_version = mi->mi_version;
131
132                 /*
133                  * uri MUST be legal!
134                  */
135                 if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) {
136                         fprintf( stderr,
137         "%s: line %d: unable to parse URI"
138         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
139                                 fname, lineno );
140                         return 1;
141                 }
142
143                 /*
144                  * uri MUST have the <dn> part!
145                  */
146                 if ( ludp->lud_dn == NULL || ludp->lud_dn[ 0 ] == '\0' ) {
147                         fprintf( stderr,
148         "%s: line %d: missing <naming context> "
149         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
150                                 fname, lineno );
151                         return 1;
152                 }
153
154                 /*
155                  * copies and stores uri and suffix
156                  */
157                 dn.bv_val = ludp->lud_dn;
158                 dn.bv_len = strlen( ludp->lud_dn );
159
160                 rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix,
161                         &mi->mi_targets[ i ].mt_nsuffix, NULL );
162                 if( rc != LDAP_SUCCESS ) {
163                         fprintf( stderr, "%s: line %d: "
164                                         "target '%s' DN is invalid\n",
165                                         fname, lineno, argv[ 1 ] );
166                         return( 1 );
167                 }
168
169                 ludp->lud_dn[ 0 ] = '\0';
170
171                 /* check all, to apply the scope check on the first one */
172                 for ( tmpludp = ludp; tmpludp; tmpludp = tmpludp->lud_next ) {
173                         if ( tmpludp->lud_dn != NULL && tmpludp->lud_dn[ 0 ] != '\0' ) {
174                                 fprintf( stderr, "%s: line %d: "
175                                                 "multiple URIs must have "
176                                                 "no DN part\n",
177                                         fname, lineno );
178                                 return( 1 );
179
180                         }
181
182                         if ( tmpludp->lud_scope == LDAP_SCOPE_BASE ) {
183                                 tmpludp->lud_scope = LDAP_SCOPE_DEFAULT;
184                         }
185                 }
186
187                 mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp );
188                 ldap_free_urllist( ludp );
189                 if ( mi->mi_targets[ i ].mt_uri == NULL) {
190                         fprintf( stderr, "%s: line %d: no memory?\n",
191                                         fname, lineno );
192                         return( 1 );
193                 }
194                 
195                 /*
196                  * uri MUST be a branch of suffix!
197                  */
198 #if 0 /* too strict a constraint */
199                 if ( select_backend( &mi->mi_targets[ i ].suffix, 0, 0 ) != be ) {
200                         fprintf( stderr,
201         "%s: line %d: <naming context> of URI does not refer to current backend"
202         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
203                                 fname, lineno );
204                         return 1;
205                 }
206 #else
207                 /*
208                  * uri MUST be a branch of a suffix!
209                  */
210                 if ( select_backend( &mi->mi_targets[ i ].mt_nsuffix, 0, 0 ) == NULL ) {
211                         fprintf( stderr,
212         "%s: line %d: <naming context> of URI does not resolve to a backend"
213         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
214                                 fname, lineno );
215                         return 1;
216                 }
217 #endif
218
219         /* default target directive */
220         } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
221                 int             i = mi->mi_ntargets - 1;
222                 
223                 if ( argc == 1 ) {
224                         if ( i < 0 ) {
225                                 fprintf( stderr,
226         "%s: line %d: \"default-target\" alone need be"
227         " inside a \"uri\" directive\n",
228                                         fname, lineno );
229                                 return 1;
230                         }
231                         mi->mi_defaulttarget = i;
232                 } else {
233                         if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) {
234                                 if ( i >= 0 ) {
235                                         fprintf( stderr,
236         "%s: line %d: \"default-target none\""
237         " should go before uri definitions\n",
238                                                 fname, lineno );
239                                 }
240                                 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
241
242                         } else {
243                                 char    *next;
244                                 int     n = strtol( argv[ 1 ], &next, 10 );
245                                 if ( n < 0 || n >= i - 1 ) {
246                                         fprintf( stderr,
247         "%s: line %d: illegal target number %d\n",
248                                                 fname, lineno, n );
249                                         return 1;
250                                 }
251                                 mi->mi_defaulttarget = n;
252                         }
253                 }
254                 
255         /* ttl of dn cache */
256         } else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) {
257                 if ( argc != 2 ) {
258                         fprintf( stderr,
259         "%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n",
260                                 fname, lineno );
261                         return 1;
262                 }
263                 
264                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
265                         mi->mi_cache.ttl = META_DNCACHE_FOREVER;
266                 } else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) {
267                         mi->mi_cache.ttl = META_DNCACHE_DISABLED;
268                 } else {
269                         mi->mi_cache.ttl = atol( argv[ 1 ] );
270                 }
271
272         /* network timeout when connecting to ldap servers */
273         } else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) {
274                 if ( argc != 2 ) {
275                         fprintf( stderr,
276         "%s: line %d: missing network timeout in \"network-timeout <seconds>\" line\n",
277                                 fname, lineno );
278                         return 1;
279                 }
280                 mi->mi_network_timeout = atol(argv[ 1 ]);
281
282         /* name to use for meta_back_group */
283         } else if ( strcasecmp( argv[ 0 ], "acl-authcDN" ) == 0
284                         || strcasecmp( argv[ 0 ], "binddn" ) == 0 )
285         {
286                 int             i = mi->mi_ntargets - 1;
287                 struct berval   dn;
288
289                 if ( i < 0 ) {
290                         fprintf( stderr,
291         "%s: line %d: need \"uri\" directive first\n",
292                                 fname, lineno );
293                         return 1;
294                 }
295                 
296                 if ( argc != 2 ) {
297                         fprintf( stderr,
298         "%s: line %d: missing name in \"binddn <name>\" line\n",
299                                 fname, lineno );
300                         return 1;
301                 }
302
303                 if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) {
304                         fprintf( stderr, "%s: line %d: "
305                                 "\"binddn\" statement is deprecated; "
306                                 "use \"acl-authcDN\" instead\n",
307                                 fname, lineno );
308                         /* FIXME: some day we'll need to throw an error */
309                 }
310
311                 dn.bv_val = argv[ 1 ];
312                 dn.bv_len = strlen( argv[ 1 ] );
313                 if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ].mt_binddn,
314                         NULL ) != LDAP_SUCCESS )
315                 {
316                         fprintf( stderr, "%s: line %d: "
317                                         "bind DN '%s' is invalid\n",
318                                         fname, lineno, argv[ 1 ] );
319                         return( 1 );
320                 }
321
322         /* password to use for meta_back_group */
323         } else if ( strcasecmp( argv[ 0 ], "acl-passwd" ) == 0
324                         || strcasecmp( argv[ 0 ], "bindpw" ) == 0 )
325         {
326                 int             i = mi->mi_ntargets - 1;
327
328                 if ( i < 0 ) {
329                         fprintf( stderr,
330         "%s: line %d: need \"uri\" directive first\n",
331                                 fname, lineno );
332                         return 1;
333                 }
334                 
335                 if ( argc != 2 ) {
336                         fprintf( stderr,
337         "%s: line %d: missing password in \"bindpw <password>\" line\n",
338                             fname, lineno );
339                         return 1;
340                 }
341
342                 if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) {
343                         fprintf( stderr, "%s: line %d: "
344                                 "\"bindpw\" statement is deprecated; "
345                                 "use \"acl-passwd\" instead\n",
346                                 fname, lineno );
347                         /* FIXME: some day we'll need to throw an error */
348                 }
349
350                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_bindpw );
351                 
352         /* save bind creds for referral rebinds? */
353         } else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) {
354                 if (argc != 1) {
355                         fprintf( stderr,
356         "%s: line %d: rebind-as-user takes no arguments\n",
357                             fname, lineno );
358                         return( 1 );
359                 }
360
361                 mi->flags |= LDAP_BACK_F_SAVECRED;
362
363         } else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) {
364                 unsigned        *flagsp = mi->mi_ntargets ?
365                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
366                                 : &mi->flags;
367
368                 if ( argc != 2 ) {
369                         fprintf( stderr,
370         "%s: line %d: \"chase-referrals\" needs 1 argument.\n",
371                                         fname, lineno );
372                         return( 1 );
373                 }
374
375                 /* this is the default; we add it because the default might change... */
376                 if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
377                         *flagsp |= LDAP_BACK_F_CHASE_REFERRALS;
378
379                 } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
380                         *flagsp &= ~LDAP_BACK_F_CHASE_REFERRALS;
381
382                 } else {
383                         fprintf( stderr,
384                 "%s: line %d: \"chase-referrals {yes|no}\": unknown argument \"%s\".\n",
385                                         fname, lineno, argv[ 1 ] );
386                         return( 1 );
387                 }
388         
389         } else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) {
390                 unsigned        *flagsp = mi->mi_ntargets ?
391                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
392                                 : &mi->flags;
393
394                 if ( argc != 2 ) {
395                         fprintf( stderr,
396                 "%s: line %d: \"tls <what>\" needs 1 argument.\n",
397                                         fname, lineno );
398                         return( 1 );
399                 }
400
401                 /* start */
402                 if ( strcasecmp( argv[ 1 ], "start" ) == 0 ) {
403                         *flagsp |= ( LDAP_BACK_F_USE_TLS | LDAP_BACK_F_TLS_CRITICAL );
404         
405                 /* try start tls */
406                 } else if ( strcasecmp( argv[ 1 ], "try-start" ) == 0 ) {
407                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
408                         *flagsp |= LDAP_BACK_F_USE_TLS;
409         
410                 /* propagate start tls */
411                 } else if ( strcasecmp( argv[ 1 ], "propagate" ) == 0 ) {
412                         *flagsp |= ( LDAP_BACK_F_PROPAGATE_TLS | LDAP_BACK_F_TLS_CRITICAL );
413                 
414                 /* try start tls */
415                 } else if ( strcasecmp( argv[ 1 ], "try-propagate" ) == 0 ) {
416                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
417                         *flagsp |= LDAP_BACK_F_PROPAGATE_TLS;
418
419                 } else {
420                         fprintf( stderr,
421                 "%s: line %d: \"tls <what>\": unknown argument \"%s\".\n",
422                                         fname, lineno, argv[ 1 ] );
423                         return( 1 );
424                 }
425
426         } else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) {
427                 unsigned        *flagsp = mi->mi_ntargets ?
428                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
429                                 : &mi->flags;
430
431                 if ( argc != 2 ) {
432                         fprintf( stderr,
433                 "%s: line %d: \"t-f-support {no|yes|discover}\" needs 1 argument.\n",
434                                         fname, lineno );
435                         return( 1 );
436                 }
437
438                 if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
439                         *flagsp &= ~(LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER);
440
441                 } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
442                         *flagsp |= LDAP_BACK_F_SUPPORT_T_F;
443
444                 } else if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
445                         *flagsp |= LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
446
447                 } else {
448                         fprintf( stderr,
449         "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
450                                 fname, lineno, argv[ 1 ] );
451                         return 1;
452                 }
453
454         /* name to use as pseudo-root dn */
455         } else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) {
456                 int             i = mi->mi_ntargets - 1;
457                 struct berval   dn;
458
459                 if ( i < 0 ) {
460                         fprintf( stderr,
461         "%s: line %d: need \"uri\" directive first\n",
462                                 fname, lineno );
463                         return 1;
464                 }
465                 
466                 if ( argc != 2 ) {
467                         fprintf( stderr,
468         "%s: line %d: missing name in \"pseudorootdn <name>\" line\n",
469                                 fname, lineno );
470                         return 1;
471                 }
472
473                 dn.bv_val = argv[ 1 ];
474                 dn.bv_len = strlen( argv[ 1 ] );
475                 if ( dnNormalize( 0, NULL, NULL, &dn,
476                         &mi->mi_targets[ i ].mt_pseudorootdn, NULL ) != LDAP_SUCCESS )
477                 {
478                         fprintf( stderr, "%s: line %d: "
479                                         "pseudoroot DN '%s' is invalid\n",
480                                         fname, lineno, argv[ 1 ] );
481                         return( 1 );
482                 }
483
484         /* password to use as pseudo-root */
485         } else if ( strcasecmp( argv[ 0 ], "pseudorootpw" ) == 0 ) {
486                 int             i = mi->mi_ntargets - 1;
487
488                 if ( i < 0 ) {
489                         fprintf( stderr,
490         "%s: line %d: need \"uri\" directive first\n",
491                                 fname, lineno );
492                         return 1;
493                 }
494                 
495                 if ( argc != 2 ) {
496                         fprintf( stderr,
497         "%s: line %d: missing password in \"pseudorootpw <password>\" line\n",
498                             fname, lineno );
499                         return 1;
500                 }
501                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_pseudorootpw );
502         
503         /* dn massaging */
504         } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
505                 BackendDB       *tmp_be;
506                 int             i = mi->mi_ntargets - 1;
507                 struct berval   dn, nvnc, pvnc, nrnc, prnc;
508
509                 if ( i < 0 ) {
510                         fprintf( stderr,
511         "%s: line %d: need \"uri\" directive first\n",
512                                 fname, lineno );
513                         return 1;
514                 }
515                 
516                 /*
517                  * syntax:
518                  * 
519                  *      suffixmassage <suffix> <massaged suffix>
520                  *
521                  * the <suffix> field must be defined as a valid suffix
522                  * (or suffixAlias?) for the current database;
523                  * the <massaged suffix> shouldn't have already been
524                  * defined as a valid suffix or suffixAlias for the 
525                  * current server
526                  */
527                 if ( argc != 3 ) {
528                         fprintf( stderr,
529         "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
530                                 fname, lineno );
531                         return 1;
532                 }
533
534                 dn.bv_val = argv[ 1 ];
535                 dn.bv_len = strlen( argv[ 1 ] );
536                 if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
537                         fprintf( stderr, "%s: line %d: "
538                                         "suffix '%s' is invalid\n",
539                                         fname, lineno, argv[ 1 ] );
540                         return 1;
541                 }
542                 
543                 tmp_be = select_backend( &nvnc, 0, 0 );
544                 if ( tmp_be != NULL && tmp_be != be ) {
545                         fprintf( stderr, 
546         "%s: line %d: suffix already in use by another backend in"
547         " \"suffixMassage <suffix> <massaged suffix>\"\n",
548                                 fname, lineno );
549                         free( pvnc.bv_val );
550                         free( nvnc.bv_val );
551                         return 1;                                               
552                 }
553
554                 dn.bv_val = argv[ 2 ];
555                 dn.bv_len = strlen( argv[ 2 ] );
556                 if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
557                         fprintf( stderr, "%s: line %d: "
558                                         "massaged suffix '%s' is invalid\n",
559                                         fname, lineno, argv[ 2 ] );
560                         free( pvnc.bv_val );
561                         free( nvnc.bv_val );
562                         return 1;
563                 }
564         
565 #if 0   
566                 tmp_be = select_backend( &nrnc, 0, 0 );
567                 if ( tmp_be != NULL ) {
568                         fprintf( stderr,
569         "%s: line %d: massaged suffix already in use by another backend in" 
570         " \"suffixMassage <suffix> <massaged suffix>\"\n",
571                                 fname, lineno );
572                         free( pvnc.bv_val );
573                         free( nvnc.bv_val );
574                         free( prnc.bv_val );
575                         free( nrnc.bv_val );
576                         return 1;
577                 }
578 #endif
579                 
580                 /*
581                  * The suffix massaging is emulated by means of the
582                  * rewrite capabilities
583                  * FIXME: no extra rewrite capabilities should be added
584                  * to the database
585                  */
586                 return suffix_massage_config( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
587                                 &pvnc, &nvnc, &prnc, &nrnc );
588                 
589         /* rewrite stuff ... */
590         } else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
591                 int             i = mi->mi_ntargets - 1;
592
593                 if ( i < 0 ) {
594                         fprintf( stderr, "%s: line %d: \"rewrite\" "
595                                 "statement outside target definition.\n",
596                                 fname, lineno );
597                         return 1;
598                 }
599                 
600                 return rewrite_parse( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
601                                 fname, lineno, argc, argv );
602
603         /* objectclass/attribute mapping */
604         } else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) {
605                 int             i = mi->mi_ntargets - 1;
606
607                 if ( i < 0 ) {
608                         fprintf( stderr,
609         "%s: line %d: need \"uri\" directive first\n",
610                                 fname, lineno );
611                         return 1;
612                 }
613
614                 return ldap_back_map_config( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, 
615                                 &mi->mi_targets[ i ].mt_rwmap.rwm_at,
616                                 fname, lineno, argc, argv );
617
618         } else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
619                 int             i = mi->mi_ntargets - 1;
620                 int             nretries = META_RETRY_UNDEFINED;
621
622                 if ( argc != 2 ) {
623                         fprintf( stderr,
624         "%s: line %d: need value in \"nretries <value>\"\n",
625                                 fname, lineno );
626                         return 1;
627                 }
628
629                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
630                         nretries = META_RETRY_FOREVER;
631
632                 } else if ( strcasecmp( argv[ 1 ], "never" ) == 0 ) {
633                         nretries = META_RETRY_NEVER;
634
635                 } else {
636                         char    *next;
637
638                         nretries = strtol( argv[ 1 ], &next, 10 );
639                         if ( next == argv[ 1 ] || next[ 0 ] != '\0' ) {
640                                 fprintf( stderr,
641         "%s: line %d: unable to parse value \"%s\" in \"nretries <value>\"\n",
642                                         fname, lineno, argv[ 1 ] );
643                                 return 1;
644                         }
645                 }
646
647                 if ( i < 0 ) {
648                         mi->mi_nretries = nretries;
649
650                 } else {
651                         mi->mi_targets[ i ].mt_nretries = nretries;
652                 }
653
654         /* anything else */
655         } else {
656                 return SLAP_CONF_UNKNOWN;
657         }
658         return 0;
659 }
660
661 int
662 ldap_back_map_config(
663                 struct ldapmap  *oc_map,
664                 struct ldapmap  *at_map,
665                 const char      *fname,
666                 int             lineno,
667                 int             argc,
668                 char            **argv )
669 {
670         struct ldapmap          *map;
671         struct ldapmapping      *mapping;
672         char                    *src, *dst;
673         int                     is_oc = 0;
674
675         if ( argc < 3 || argc > 4 ) {
676                 fprintf( stderr,
677         "%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
678                         fname, lineno );
679                 return 1;
680         }
681
682         if ( strcasecmp( argv[ 1 ], "objectclass" ) == 0 ) {
683                 map = oc_map;
684                 is_oc = 1;
685
686         } else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) {
687                 map = at_map;
688
689         } else {
690                 fprintf( stderr, "%s: line %d: syntax is "
691                         "\"map {objectclass | attribute} [<local> | *] "
692                         "{<foreign> | *}\"\n",
693                         fname, lineno );
694                 return 1;
695         }
696
697         if ( strcmp( argv[ 2 ], "*" ) == 0 ) {
698                 if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) {
699                         map->drop_missing = ( argc < 4 );
700                         return 0;
701                 }
702                 src = dst = argv[ 3 ];
703
704         } else if ( argc < 4 ) {
705                 src = "";
706                 dst = argv[ 2 ];
707
708         } else {
709                 src = argv[ 2 ];
710                 dst = ( strcmp( argv[ 3 ], "*" ) == 0 ? src : argv[ 3 ] );
711         }
712
713         if ( ( map == at_map )
714                         && ( strcasecmp( src, "objectclass" ) == 0
715                         || strcasecmp( dst, "objectclass" ) == 0 ) )
716         {
717                 fprintf( stderr,
718                         "%s: line %d: objectclass attribute cannot be mapped\n",
719                         fname, lineno );
720         }
721
722         mapping = (struct ldapmapping *)ch_calloc( 2,
723                 sizeof(struct ldapmapping) );
724         if ( mapping == NULL ) {
725                 fprintf( stderr,
726                         "%s: line %d: out of memory\n",
727                         fname, lineno );
728                 return 1;
729         }
730         ber_str2bv( src, 0, 1, &mapping->src );
731         ber_str2bv( dst, 0, 1, &mapping->dst );
732         mapping[ 1 ].src = mapping->dst;
733         mapping[ 1 ].dst = mapping->src;
734
735         /*
736          * schema check
737          */
738         if ( is_oc ) {
739                 if ( src[ 0 ] != '\0' ) {
740                         if ( oc_bvfind( &mapping->src ) == NULL ) {
741                                 fprintf( stderr,
742         "%s: line %d: warning, source objectClass '%s' "
743         "should be defined in schema\n",
744                                         fname, lineno, src );
745
746                                 /*
747                                  * FIXME: this should become an err
748                                  */
749                                 goto error_return;
750                         }
751                 }
752
753                 if ( oc_bvfind( &mapping->dst ) == NULL ) {
754                         fprintf( stderr,
755         "%s: line %d: warning, destination objectClass '%s' "
756         "is not defined in schema\n",
757                                 fname, lineno, dst );
758                 }
759         } else {
760                 int                     rc;
761                 const char              *text = NULL;
762                 AttributeDescription    *ad = NULL;
763
764                 if ( src[ 0 ] != '\0' ) {
765                         rc = slap_bv2ad( &mapping->src, &ad, &text );
766                         if ( rc != LDAP_SUCCESS ) {
767                                 fprintf( stderr,
768         "%s: line %d: warning, source attributeType '%s' "
769         "should be defined in schema\n",
770                                         fname, lineno, src );
771
772                                 /*
773                                  * FIXME: this should become an err
774                                  */
775                                 goto error_return;
776                         }
777
778                         ad = NULL;
779                 }
780
781                 rc = slap_bv2ad( &mapping->dst, &ad, &text );
782                 if ( rc != LDAP_SUCCESS ) {
783                         fprintf( stderr,
784         "%s: line %d: warning, destination attributeType '%s' "
785         "is not defined in schema\n",
786                                 fname, lineno, dst );
787                 }
788         }
789
790         if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)mapping, mapping_cmp ) != NULL)
791                         || avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
792         {
793                 fprintf( stderr,
794                         "%s: line %d: duplicate mapping found" SLAPD_CONF_UNKNOWN_IGNORED ".\n",
795                         fname, lineno );
796                 goto error_return;
797         }
798
799         if ( src[ 0 ] != '\0' ) {
800                 avl_insert( &map->map, (caddr_t)mapping,
801                                         mapping_cmp, mapping_dup );
802         }
803         avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
804                                 mapping_cmp, mapping_dup );
805
806         return 0;
807
808 error_return:;
809         if ( mapping ) {
810                 ch_free( mapping->src.bv_val );
811                 ch_free( mapping->dst.bv_val );
812                 ch_free( mapping );
813         }
814
815         return 1;
816 }
817
818
819 #ifdef ENABLE_REWRITE
820 static char *
821 suffix_massage_regexize( const char *s )
822 {
823         char *res, *ptr;
824         const char *p, *r;
825         int i;
826
827         for ( i = 0, p = s; 
828                         ( r = strchr( p, ',' ) ) != NULL; 
829                         p = r + 1, i++ )
830                 ;
831
832         res = ch_calloc( sizeof( char ),
833                         strlen( s )
834                         + STRLENOF( "(.+,)?" )
835                         + STRLENOF( "[ ]?" ) * i + 1 );
836
837         ptr = lutil_strcopy( res, "(.+,)?" );
838         for ( i = 0, p = s;
839                         ( r = strchr( p, ',' ) ) != NULL;
840                         p = r + 1 , i++ ) {
841                 ptr = lutil_strncopy( ptr, p, r - p + 1 );
842                 ptr = lutil_strcopy( ptr, "[ ]?" );
843
844                 if ( r[ 1 ] == ' ' ) {
845                         r++;
846                 }
847         }
848         lutil_strcopy( ptr, p );
849
850         return res;
851 }
852
853 static char *
854 suffix_massage_patternize( const char *s )
855 {
856         ber_len_t       len;
857         char            *res;
858
859         len = strlen( s );
860
861         res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
862         if ( res == NULL ) {
863                 return NULL;
864         }
865
866         strcpy( res, "%1" );
867         strcpy( &res[ STRLENOF( "%1" ) ], s );
868
869         return res;
870 }
871
872 int
873 suffix_massage_config( 
874                 struct rewrite_info *info,
875                 struct berval *pvnc,
876                 struct berval *nvnc,
877                 struct berval *prnc,
878                 struct berval *nrnc
879 )
880 {
881         char *rargv[ 5 ];
882         int line = 0;
883
884         rargv[ 0 ] = "rewriteEngine";
885         rargv[ 1 ] = "on";
886         rargv[ 2 ] = NULL;
887         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
888
889         rargv[ 0 ] = "rewriteContext";
890         rargv[ 1 ] = "default";
891         rargv[ 2 ] = NULL;
892         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
893
894         rargv[ 0 ] = "rewriteRule";
895         rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
896         rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val );
897         rargv[ 3 ] = ":";
898         rargv[ 4 ] = NULL;
899         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
900         ch_free( rargv[ 1 ] );
901         ch_free( rargv[ 2 ] );
902         
903         rargv[ 0 ] = "rewriteContext";
904         rargv[ 1 ] = "searchEntryDN";
905         rargv[ 2 ] = NULL;
906         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
907
908         rargv[ 0 ] = "rewriteRule";
909         rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
910         rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val );
911         rargv[ 3 ] = ":";
912         rargv[ 4 ] = NULL;
913         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
914         ch_free( rargv[ 1 ] );
915         ch_free( rargv[ 2 ] );
916
917         /* backward compatibility */
918         rargv[ 0 ] = "rewriteContext";
919         rargv[ 1 ] = "searchResult";
920         rargv[ 2 ] = "alias";
921         rargv[ 3 ] = "searchEntryDN";
922         rargv[ 4 ] = NULL;
923         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
924         
925         rargv[ 0 ] = "rewriteContext";
926         rargv[ 1 ] = "matchedDN";
927         rargv[ 2 ] = "alias";
928         rargv[ 3 ] = "searchEntryDN";
929         rargv[ 4 ] = NULL;
930         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
931
932         rargv[ 0 ] = "rewriteContext";
933         rargv[ 1 ] = "searchAttrDN";
934         rargv[ 2 ] = "alias";
935         rargv[ 3 ] = "searchEntryDN";
936         rargv[ 4 ] = NULL;
937         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
938
939         /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
940          * see servers/slapd/overlays/rwm.h for details */
941         rargv[ 0 ] = "rewriteContext";
942         rargv[ 1 ] = "referralAttrDN";
943         rargv[ 2 ] = NULL;
944         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
945
946         rargv[ 0 ] = "rewriteContext";
947         rargv[ 1 ] = "referralDN";
948         rargv[ 2 ] = NULL;
949         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
950         
951         return 0;
952 }
953 #endif /* ENABLE_REWRITE */
954