]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/config.c
import selected fixes/enhancements from HEAD (ITS#4387, partial ITS#4390)
[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-2006 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 meta_back_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         assert( mi != NULL );
99
100         /* URI of server to query */
101         if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
102                 int             i = mi->mi_ntargets;
103 #if 0
104                 int             j;
105 #endif /* uncomment if uri MUST be a branch of suffix */
106                 LDAPURLDesc     *ludp, *tmpludp;
107                 struct berval   dn;
108                 int             rc;
109                 int             c;
110                 
111                 switch ( argc ) {
112                 case 1:
113                         Debug( LDAP_DEBUG_ANY,
114         "%s: line %d: missing URI "
115         "in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
116                                 fname, lineno, 0 );
117                         return 1;
118
119                 case 2:
120                         break;
121
122                 default:
123                         Debug( LDAP_DEBUG_ANY,
124         "%s: line %d: too many args "
125         "in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
126                                 fname, lineno, 0 );
127                         return 1;
128                 }
129
130                 if ( be->be_nsuffix == NULL ) {
131                         Debug( LDAP_DEBUG_ANY,
132         "%s: line %d: the suffix must be defined before any target.\n",
133                                 fname, lineno, 0 );
134                         return 1;
135                 }
136                 
137                 ++mi->mi_ntargets;
138
139                 mi->mi_targets = ( metatarget_t * )ch_realloc( mi->mi_targets, 
140                         sizeof( metatarget_t ) * mi->mi_ntargets );
141                 if ( mi->mi_targets == NULL ) {
142                         Debug( LDAP_DEBUG_ANY,
143         "%s: line %d: out of memory while storing server name"
144         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
145                                 fname, lineno, 0 );
146                         return 1;
147                 }
148
149                 if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
150                         Debug( LDAP_DEBUG_ANY,
151         "%s: line %d: unable to init server"
152         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
153                                 fname, lineno, 0 );
154                         return 1;
155                 }
156
157                 mi->mi_targets[ i ].mt_nretries = mi->mi_nretries;
158                 mi->mi_targets[ i ].mt_flags = mi->mi_flags;
159                 mi->mi_targets[ i ].mt_version = mi->mi_version;
160                 mi->mi_targets[ i ].mt_network_timeout = mi->mi_network_timeout;
161                 mi->mi_targets[ i ].mt_conn_ttl = mi->mi_conn_ttl;
162                 mi->mi_targets[ i ].mt_idle_timeout = mi->mi_idle_timeout;
163                 mi->mi_targets[ i ].mt_bind_timeout = mi->mi_bind_timeout;
164                 for ( c = 0; c < LDAP_BACK_OP_LAST; c++ ) {
165                         mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ];
166                 }
167
168                 /*
169                  * uri MUST be legal!
170                  */
171                 if ( ldap_url_parselist_ext( &ludp, argv[ 1 ], "\t" ) != LDAP_SUCCESS ) {
172                         Debug( LDAP_DEBUG_ANY,
173         "%s: line %d: unable to parse URI"
174         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
175                                 fname, lineno, 0 );
176                         return 1;
177                 }
178
179                 /*
180                  * uri MUST have the <dn> part!
181                  */
182                 if ( ludp->lud_dn == NULL ) {
183                         Debug( LDAP_DEBUG_ANY,
184         "%s: line %d: missing <naming context> "
185         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
186                                 fname, lineno, 0 );
187                         return 1;
188
189                 } else if ( ludp->lud_dn[ 0 ] == '\0' ) {
190                         int     j = -1;
191
192                         for ( j = 0; !BER_BVISNULL( &be->be_nsuffix[ j ] ); j++ ) {
193                                 if ( BER_BVISEMPTY( &be->be_nsuffix[ j ] ) ) {
194                                         break;
195                                 }
196                         }
197
198                         if ( BER_BVISNULL( &be->be_nsuffix[ j ] ) ) {
199                                 Debug( LDAP_DEBUG_ANY,
200                 "%s: line %d: missing <naming context> "
201                 " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
202                                         fname, lineno, 0 );
203                                 return 1;
204                         }
205                 }
206
207                 /*
208                  * copies and stores uri and suffix
209                  */
210                 ber_str2bv( ludp->lud_dn, 0, 0, &dn );
211                 rc = dnPrettyNormal( NULL, &dn, &mi->mi_targets[ i ].mt_psuffix,
212                         &mi->mi_targets[ i ].mt_nsuffix, NULL );
213                 if( rc != LDAP_SUCCESS ) {
214                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
215                                 "target \"%s\" DN is invalid\n",
216                                 fname, lineno, argv[ 1 ] );
217                         return( 1 );
218                 }
219
220                 ludp->lud_dn[ 0 ] = '\0';
221
222                 switch ( ludp->lud_scope ) {
223                 case LDAP_SCOPE_DEFAULT:
224                         mi->mi_targets[ i ].mt_scope = LDAP_SCOPE_SUBTREE;
225                         break;
226
227                 case LDAP_SCOPE_SUBTREE:
228                 case LDAP_SCOPE_SUBORDINATE:
229                         mi->mi_targets[ i ].mt_scope = ludp->lud_scope;
230                         break;
231
232                 default:
233                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
234                                 "invalid scope for target \"%s\"\n",
235                                 fname, lineno, argv[ 1 ] );
236                         return( 1 );
237                 }
238
239                 /* check all, to apply the scope check on the first one */
240                 for ( tmpludp = ludp; tmpludp; tmpludp = tmpludp->lud_next ) {
241                         if ( tmpludp->lud_dn != NULL && tmpludp->lud_dn[ 0 ] != '\0' ) {
242                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
243                                         "multiple URIs must have "
244                                         "no DN part\n",
245                                         fname, lineno, 0 );
246                                 return( 1 );
247
248                         }
249                 }
250
251                 mi->mi_targets[ i ].mt_uri = ldap_url_list2urls( ludp );
252                 ldap_free_urllist( ludp );
253                 if ( mi->mi_targets[ i ].mt_uri == NULL) {
254                         Debug( LDAP_DEBUG_ANY, "%s: line %d: no memory?\n",
255                                 fname, lineno, 0 );
256                         return( 1 );
257                 }
258                 
259                 /*
260                  * uri MUST be a branch of suffix!
261                  */
262 #if 0 /* too strict a constraint */
263                 if ( select_backend( &mi->mi_targets[ i ].suffix, 0, 0 ) != be ) {
264                         Debug( LDAP_DEBUG_ANY,
265         "%s: line %d: <naming context> of URI does not refer to current backend"
266         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
267                                 fname, lineno, 0 );
268                         return 1;
269                 }
270 #else
271                 /*
272                  * uri MUST be a branch of a suffix!
273                  */
274                 if ( select_backend( &mi->mi_targets[ i ].mt_nsuffix, 0, 0 ) == NULL ) {
275                         Debug( LDAP_DEBUG_ANY,
276         "%s: line %d: <naming context> of URI does not resolve to a backend"
277         " in \"uri <protocol>://<server>[:port]/<naming context>\" line\n",
278                                 fname, lineno, 0 );
279                         return 1;
280                 }
281 #endif
282
283         /* subtree-exclude */
284         } else if ( strcasecmp( argv[ 0 ], "subtree-exclude" ) == 0 ) {
285                 int             i = mi->mi_ntargets - 1;
286                 struct berval   dn, ndn;
287
288                 if ( i < 0 ) {
289                         Debug( LDAP_DEBUG_ANY,
290         "%s: line %d: need \"uri\" directive first\n",
291                                 fname, lineno, 0 );
292                         return 1;
293                 }
294                 
295                 switch ( argc ) {
296                 case 1:
297                         Debug( LDAP_DEBUG_ANY,
298         "%s: line %d: missing DN in \"subtree-exclude <DN>\" line\n",
299                             fname, lineno, 0 );
300                         return 1;
301
302                 case 2:
303                         break;
304
305                 default:
306                         Debug( LDAP_DEBUG_ANY,
307         "%s: line %d: too many args in \"subtree-exclude <DN>\" line\n",
308                             fname, lineno, 0 );
309                         return 1;
310                 }
311
312                 ber_str2bv( argv[ 1 ], 0, 0, &dn );
313                 if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
314                         != LDAP_SUCCESS )
315                 {
316                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
317                                         "subtree-exclude DN=\"%s\" is invalid\n",
318                                         fname, lineno, argv[ 1 ] );
319                         return( 1 );
320                 }
321
322                 if ( !dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_nsuffix ) ) {
323                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
324                                         "subtree-exclude DN=\"%s\" "
325                                         "must be subtree of target\n",
326                                         fname, lineno, argv[ 1 ] );
327                         ber_memfree( ndn.bv_val );
328                         return( 1 );
329                 }
330
331                 if ( mi->mi_targets[ i ].mt_subtree_exclude != NULL ) {
332                         int             j;
333
334                         for ( j = 0; !BER_BVISNULL( &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ); j++ )
335                         {
336                                 if ( dnIsSuffix( &mi->mi_targets[ i ].mt_subtree_exclude[ j ], &ndn ) ) {
337                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
338                                                         "subtree-exclude DN=\"%s\" "
339                                                         "is suffix of another subtree-exclude\n",
340                                                         fname, lineno, argv[ 1 ] );
341                                         /* reject, because it might be superior
342                                          * to more than one subtree-exclude */
343                                         ber_memfree( ndn.bv_val );
344                                         return( 1 );
345
346                                 } else if ( dnIsSuffix( &ndn, &mi->mi_targets[ i ].mt_subtree_exclude[ j ] ) ) {
347                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
348                                                         "another subtree-exclude is suffix of "
349                                                         "subtree-exclude DN=\"%s\"\n",
350                                                         fname, lineno, argv[ 1 ] );
351                                         ber_memfree( ndn.bv_val );
352                                         return( 0 );
353                                 }
354                         }
355                 }
356
357                 ber_bvarray_add( &mi->mi_targets[ i ].mt_subtree_exclude, &ndn );
358
359         /* default target directive */
360         } else if ( strcasecmp( argv[ 0 ], "default-target" ) == 0 ) {
361                 int             i = mi->mi_ntargets - 1;
362                 
363                 if ( argc == 1 ) {
364                         if ( i < 0 ) {
365                                 Debug( LDAP_DEBUG_ANY,
366         "%s: line %d: \"default-target\" alone need be"
367         " inside a \"uri\" directive\n",
368                                         fname, lineno, 0 );
369                                 return 1;
370                         }
371                         mi->mi_defaulttarget = i;
372
373                 } else {
374                         if ( strcasecmp( argv[ 1 ], "none" ) == 0 ) {
375                                 if ( i >= 0 ) {
376                                         Debug( LDAP_DEBUG_ANY,
377         "%s: line %d: \"default-target none\""
378         " should go before uri definitions\n",
379                                                 fname, lineno, 0 );
380                                 }
381                                 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
382
383                         } else {
384                                 
385                                 if ( lutil_atoi( &mi->mi_defaulttarget, argv[ 1 ] ) != 0
386                                         || mi->mi_defaulttarget < 0
387                                         || mi->mi_defaulttarget >= i - 1 )
388                                 {
389                                         Debug( LDAP_DEBUG_ANY,
390         "%s: line %d: illegal target number %d\n",
391                                                 fname, lineno, mi->mi_defaulttarget );
392                                         return 1;
393                                 }
394                         }
395                 }
396                 
397         /* ttl of dn cache */
398         } else if ( strcasecmp( argv[ 0 ], "dncache-ttl" ) == 0 ) {
399                 if ( argc != 2 ) {
400                         Debug( LDAP_DEBUG_ANY,
401         "%s: line %d: missing ttl in \"dncache-ttl <ttl>\" line\n",
402                                 fname, lineno, 0 );
403                         return 1;
404                 }
405                 
406                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
407                         mi->mi_cache.ttl = META_DNCACHE_FOREVER;
408
409                 } else if ( strcasecmp( argv[ 1 ], "disabled" ) == 0 ) {
410                         mi->mi_cache.ttl = META_DNCACHE_DISABLED;
411
412                 } else {
413                         unsigned long   t;
414
415                         if ( lutil_parse_time( argv[ 1 ], &t ) != 0 ) {
416                                 Debug( LDAP_DEBUG_ANY,
417         "%s: line %d: unable to parse ttl \"%s\" in \"dncache-ttl <ttl>\" line\n",
418                                         fname, lineno, argv[ 1 ] );
419                                 return 1;
420                         }
421                         mi->mi_cache.ttl = (time_t)t;
422                 }
423
424         /* network timeout when connecting to ldap servers */
425         } else if ( strcasecmp( argv[ 0 ], "network-timeout" ) == 0 ) {
426                 unsigned long   t;
427                 time_t          *tp = mi->mi_ntargets ?
428                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_network_timeout
429                                 : &mi->mi_network_timeout;
430
431                 if ( argc != 2 ) {
432                         Debug( LDAP_DEBUG_ANY,
433         "%s: line %d: missing network timeout in \"network-timeout <seconds>\" line\n",
434                                 fname, lineno, 0 );
435                         return 1;
436                 }
437
438                 if ( lutil_parse_time( argv[ 1 ], &t ) ) {
439                         Debug( LDAP_DEBUG_ANY,
440         "%s: line %d: unable to parse timeout \"%s\" in \"network-timeout <seconds>\" line\n",
441                                 fname, lineno, argv[ 1 ] );
442                         return 1;
443
444                 }
445
446                 *tp = (time_t)t;
447
448         /* idle timeout when connecting to ldap servers */
449         } else if ( strcasecmp( argv[ 0 ], "idle-timeout" ) == 0 ) {
450                 unsigned long   t;
451                 time_t          *tp = mi->mi_ntargets ?
452                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_idle_timeout
453                                 : &mi->mi_idle_timeout;
454
455                 switch ( argc ) {
456                 case 1:
457                         Debug( LDAP_DEBUG_ANY,
458         "%s: line %d: missing timeout value in \"idle-timeout <seconds>\" line\n",
459                                 fname, lineno, 0 );
460                         return 1;
461                 case 2:
462                         break;
463                 default:
464                         Debug( LDAP_DEBUG_ANY,
465         "%s: line %d: extra cruft after timeout value in \"idle-timeout <seconds>\" line\n",
466                                 fname, lineno, 0 );
467                         return 1;
468                 }
469
470                 if ( lutil_parse_time( argv[ 1 ], &t ) ) {
471                         Debug( LDAP_DEBUG_ANY,
472         "%s: line %d: unable to parse timeout \"%s\" in \"idle-timeout <seconds>\" line\n",
473                                 fname, lineno, argv[ 1 ] );
474                         return 1;
475
476                 }
477
478                 *tp = (time_t)t;
479
480         /* conn ttl */
481         } else if ( strcasecmp( argv[ 0 ], "conn-ttl" ) == 0 ) {
482                 unsigned long   t;
483                 time_t          *tp = mi->mi_ntargets ?
484                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_conn_ttl
485                                 : &mi->mi_conn_ttl;
486
487                 switch ( argc ) {
488                 case 1:
489                         Debug( LDAP_DEBUG_ANY,
490         "%s: line %d: missing ttl value in \"conn-ttl <seconds>\" line\n",
491                                 fname, lineno, 0 );
492                         return 1;
493                 case 2:
494                         break;
495                 default:
496                         Debug( LDAP_DEBUG_ANY,
497         "%s: line %d: extra cruft after ttl value in \"conn-ttl <seconds>\" line\n",
498                                 fname, lineno, 0 );
499                         return 1;
500                 }
501
502                 if ( lutil_parse_time( argv[ 1 ], &t ) ) {
503                         Debug( LDAP_DEBUG_ANY,
504         "%s: line %d: unable to parse ttl \"%s\" in \"conn-ttl <seconds>\" line\n",
505                                 fname, lineno, argv[ 1 ] );
506                         return 1;
507
508                 }
509
510                 *tp = (time_t)t;
511
512         /* bind timeout when connecting to ldap servers */
513         } else if ( strcasecmp( argv[ 0 ], "bind-timeout" ) == 0 ) {
514                 unsigned long   t;
515                 struct timeval  *tp = mi->mi_ntargets ?
516                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_bind_timeout
517                                 : &mi->mi_bind_timeout;
518
519                 switch ( argc ) {
520                 case 1:
521                         Debug( LDAP_DEBUG_ANY,
522         "%s: line %d: missing timeout value in \"bind-timeout <microseconds>\" line\n",
523                                 fname, lineno, 0 );
524                         return 1;
525                 case 2:
526                         break;
527                 default:
528                         Debug( LDAP_DEBUG_ANY,
529         "%s: line %d: extra cruft after timeout value in \"bind-timeout <microseconds>\" line\n",
530                                 fname, lineno, 0 );
531                         return 1;
532                 }
533
534                 if ( lutil_atoul( &t, argv[ 1 ] ) != 0 ) {
535                         Debug( LDAP_DEBUG_ANY,
536         "%s: line %d: unable to parse timeout \"%s\" in \"bind-timeout <microseconds>\" line\n",
537                                 fname, lineno, argv[ 1 ] );
538                         return 1;
539
540                 }
541
542                 tp->tv_sec = t/1000000;
543                 tp->tv_usec = t%1000000;
544
545         /* name to use for meta_back_group */
546         } else if ( strcasecmp( argv[ 0 ], "acl-authcDN" ) == 0
547                         || strcasecmp( argv[ 0 ], "binddn" ) == 0 )
548         {
549                 int             i = mi->mi_ntargets - 1;
550                 struct berval   dn;
551
552                 if ( i < 0 ) {
553                         Debug( LDAP_DEBUG_ANY,
554         "%s: line %d: need \"uri\" directive first\n",
555                                 fname, lineno, 0 );
556                         return 1;
557                 }
558                 
559                 if ( argc != 2 ) {
560                         Debug( LDAP_DEBUG_ANY,
561         "%s: line %d: missing name in \"binddn <name>\" line\n",
562                                 fname, lineno, 0 );
563                         return 1;
564                 }
565
566                 if ( strcasecmp( argv[ 0 ], "binddn" ) == 0 ) {
567                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
568                                 "\"binddn\" statement is deprecated; "
569                                 "use \"acl-authcDN\" instead\n",
570                                 fname, lineno, 0 );
571                         /* FIXME: some day we'll need to throw an error */
572                 }
573
574                 ber_str2bv( argv[ 1 ], 0, 0, &dn );
575                 if ( dnNormalize( 0, NULL, NULL, &dn, &mi->mi_targets[ i ].mt_binddn,
576                         NULL ) != LDAP_SUCCESS )
577                 {
578                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
579                                         "bind DN '%s' is invalid\n",
580                                         fname, lineno, argv[ 1 ] );
581                         return( 1 );
582                 }
583
584         /* password to use for meta_back_group */
585         } else if ( strcasecmp( argv[ 0 ], "acl-passwd" ) == 0
586                         || strcasecmp( argv[ 0 ], "bindpw" ) == 0 )
587         {
588                 int             i = mi->mi_ntargets - 1;
589
590                 if ( i < 0 ) {
591                         Debug( LDAP_DEBUG_ANY,
592         "%s: line %d: need \"uri\" directive first\n",
593                                 fname, lineno, 0 );
594                         return 1;
595                 }
596                 
597                 if ( argc != 2 ) {
598                         Debug( LDAP_DEBUG_ANY,
599         "%s: line %d: missing password in \"bindpw <password>\" line\n",
600                             fname, lineno, 0 );
601                         return 1;
602                 }
603
604                 if ( strcasecmp( argv[ 0 ], "bindpw" ) == 0 ) {
605                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
606                                 "\"bindpw\" statement is deprecated; "
607                                 "use \"acl-passwd\" instead\n",
608                                 fname, lineno, 0 );
609                         /* FIXME: some day we'll need to throw an error */
610                 }
611
612                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_bindpw );
613                 
614         /* save bind creds for referral rebinds? */
615         } else if ( strcasecmp( argv[ 0 ], "rebind-as-user" ) == 0 ) {
616                 if ( argc > 2 ) {
617                         Debug( LDAP_DEBUG_ANY,
618         "%s: line %d: \"rebind-as-user {NO|yes}\" takes 1 argument.\n",
619                             fname, lineno, 0 );
620                         return( 1 );
621                 }
622
623                 if ( argc == 1 ) {
624                         Debug( LDAP_DEBUG_ANY,
625         "%s: line %d: deprecated use of \"rebind-as-user {FALSE|true}\" with no arguments.\n",
626                             fname, lineno, 0 );
627                         mi->mi_flags |= LDAP_BACK_F_SAVECRED;
628
629                 } else {
630                         switch ( check_true_false( argv[ 1 ] ) ) {
631                         case 0:
632                                 mi->mi_flags &= ~LDAP_BACK_F_SAVECRED;
633                                 break;
634
635                         case 1:
636                                 mi->mi_flags |= LDAP_BACK_F_SAVECRED;
637                                 break;
638
639                         default:
640                                 Debug( LDAP_DEBUG_ANY,
641         "%s: line %d: \"rebind-as-user {FALSE|true}\" unknown argument \"%s\".\n",
642                                     fname, lineno, argv[ 1 ] );
643                                 return 1;
644                         }
645                 }
646
647         } else if ( strcasecmp( argv[ 0 ], "chase-referrals" ) == 0 ) {
648                 unsigned        *flagsp = mi->mi_ntargets ?
649                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
650                                 : &mi->mi_flags;
651
652                 if ( argc != 2 ) {
653                         Debug( LDAP_DEBUG_ANY,
654         "%s: line %d: \"chase-referrals {TRUE|false}\" needs 1 argument.\n",
655                                 fname, lineno, 0 );
656                         return( 1 );
657                 }
658
659                 /* this is the default; we add it because the default might change... */
660                 switch ( check_true_false( argv[ 1 ] ) ) {
661                 case 1:
662                         *flagsp |= LDAP_BACK_F_CHASE_REFERRALS;
663                         break;
664
665                 case 0:
666                         *flagsp &= ~LDAP_BACK_F_CHASE_REFERRALS;
667                         break;
668
669                 default:
670                         Debug( LDAP_DEBUG_ANY,
671                 "%s: line %d: \"chase-referrals {TRUE|false}\": unknown argument \"%s\".\n",
672                                 fname, lineno, argv[ 1 ] );
673                         return( 1 );
674                 }
675         
676         } else if ( strcasecmp( argv[ 0 ], "tls" ) == 0 ) {
677                 unsigned        *flagsp = mi->mi_ntargets ?
678                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
679                                 : &mi->mi_flags;
680
681                 if ( argc != 2 ) {
682                         Debug( LDAP_DEBUG_ANY,
683                 "%s: line %d: \"tls <what>\" needs 1 argument.\n",
684                                 fname, lineno, 0 );
685                         return( 1 );
686                 }
687
688                 /* start */
689                 if ( strcasecmp( argv[ 1 ], "start" ) == 0 ) {
690                         *flagsp |= ( LDAP_BACK_F_USE_TLS | LDAP_BACK_F_TLS_CRITICAL );
691         
692                 /* try start tls */
693                 } else if ( strcasecmp( argv[ 1 ], "try-start" ) == 0 ) {
694                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
695                         *flagsp |= LDAP_BACK_F_USE_TLS;
696         
697                 /* propagate start tls */
698                 } else if ( strcasecmp( argv[ 1 ], "propagate" ) == 0 ) {
699                         *flagsp |= ( LDAP_BACK_F_PROPAGATE_TLS | LDAP_BACK_F_TLS_CRITICAL );
700                 
701                 /* try start tls */
702                 } else if ( strcasecmp( argv[ 1 ], "try-propagate" ) == 0 ) {
703                         *flagsp &= ~LDAP_BACK_F_TLS_CRITICAL;
704                         *flagsp |= LDAP_BACK_F_PROPAGATE_TLS;
705
706                 } else {
707                         Debug( LDAP_DEBUG_ANY,
708                 "%s: line %d: \"tls <what>\": unknown argument \"%s\".\n",
709                                 fname, lineno, argv[ 1 ] );
710                         return( 1 );
711                 }
712
713         } else if ( strcasecmp( argv[ 0 ], "t-f-support" ) == 0 ) {
714                 unsigned        *flagsp = mi->mi_ntargets ?
715                                 &mi->mi_targets[ mi->mi_ntargets - 1 ].mt_flags
716                                 : &mi->mi_flags;
717
718                 if ( argc != 2 ) {
719                         Debug( LDAP_DEBUG_ANY,
720                 "%s: line %d: \"t-f-support {FALSE|true|discover}\" needs 1 argument.\n",
721                                 fname, lineno, 0 );
722                         return( 1 );
723                 }
724
725                 switch ( check_true_false( argv[ 1 ] ) ) {
726                 case 0:
727                         *flagsp &= ~(LDAP_BACK_F_SUPPORT_T_F|LDAP_BACK_F_SUPPORT_T_F_DISCOVER);
728                         break;
729
730                 case 1:
731                         *flagsp |= LDAP_BACK_F_SUPPORT_T_F;
732                         break;
733
734                 default:
735                         if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
736                                 *flagsp |= LDAP_BACK_F_SUPPORT_T_F_DISCOVER;
737
738                         } else {
739                                 Debug( LDAP_DEBUG_ANY,
740         "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
741                                         fname, lineno, argv[ 1 ] );
742                                 return 1;
743                         }
744                         break;
745                 }
746
747         /* onerr? */
748         } else if ( strcasecmp( argv[ 0 ], "onerr" ) == 0 ) {
749                 if ( argc != 2 ) {
750                         Debug( LDAP_DEBUG_ANY,
751         "%s: line %d: \"onerr {CONTINUE|stop}\" takes 1 argument\n",
752                                 fname, lineno, 0 );
753                         return( 1 );
754                 }
755
756                 if ( strcasecmp( argv[ 1 ], "continue" ) == 0 ) {
757                         mi->mi_flags &= ~META_BACK_F_ONERR_STOP;
758
759                 } else if ( strcasecmp( argv[ 1 ], "stop" ) == 0 ) {
760                         mi->mi_flags |= META_BACK_F_ONERR_STOP;
761
762                 } else {
763                         Debug( LDAP_DEBUG_ANY,
764         "%s: line %d: \"onerr {CONTINUE|stop}\": invalid arg \"%s\".\n",
765                                 fname, lineno, argv[ 1 ] );
766                         return 1;
767                 }
768
769         /* bind-defer? */
770         } else if ( strcasecmp( argv[ 0 ], "pseudoroot-bind-defer" ) == 0 ) {
771                 if ( argc != 2 ) {
772                         Debug( LDAP_DEBUG_ANY,
773         "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\" takes 1 argument\n",
774                                 fname, lineno, 0 );
775                         return( 1 );
776                 }
777
778                 switch ( check_true_false( argv[ 1 ] ) ) {
779                 case 0:
780                         mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
781                         break;
782
783                 case 1:
784                         mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
785                         break;
786
787                 default:
788                         Debug( LDAP_DEBUG_ANY,
789         "%s: line %d: \"pseudoroot-bind-defer {FALSE|true}\": invalid arg \"%s\".\n",
790                                 fname, lineno, argv[ 1 ] );
791                         return 1;
792                 }
793
794         } else if ( strcasecmp( argv[ 0 ], "timeout" ) == 0 ) {
795                 char    *sep;
796                 time_t  *tv = mi->mi_ntargets ?
797                                 mi->mi_targets[ mi->mi_ntargets - 1 ].mt_timeout
798                                 : mi->mi_timeout;
799                 int     c;
800
801                 if ( argc < 2 ) {
802                         Debug( LDAP_DEBUG_ANY,
803         "%s: line %d: \"timeout [{add|delete|modify|modrdn}=]<val> [...]\" takes at least 1 argument\n",
804                                 fname, lineno, 0 );
805                         return( 1 );
806                 }
807
808                 for ( c = 1; c < argc; c++ ) {
809                         time_t          *t = NULL;
810                         unsigned long   val;
811
812                         sep = strchr( argv[ c ], '=' );
813                         if ( sep != NULL ) {
814                                 size_t  len = sep - argv[ c ];
815
816                                 if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
817                                         t = &tv[ LDAP_BACK_OP_ADD ];
818                                 } else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
819                                         t = &tv[ LDAP_BACK_OP_DELETE ];
820                                 } else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
821                                         t = &tv[ LDAP_BACK_OP_MODIFY ];
822                                 } else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
823                                         t = &tv[ LDAP_BACK_OP_MODRDN ];
824                                 } else {
825                                         char    buf[ SLAP_TEXT_BUFLEN ];
826                                         snprintf( buf, sizeof( buf ),
827                                                 "unknown operation \"%s\" for timeout #%d",
828                                                 argv[ c ], c );
829                                         Debug( LDAP_DEBUG_ANY,
830                                                 "%s: line %d: %s.\n",
831                                                 fname, lineno, buf );
832                                         return 1;
833                                 }
834                                 sep++;
835         
836                         } else {
837                                 sep = argv[ c ];
838                         }
839         
840                         if ( lutil_parse_time( sep, &val ) != 0 ) {
841                                 Debug( LDAP_DEBUG_ANY,
842                 "%s: line %d: unable to parse value \"%s\" for timeout.\n",
843                                         fname, lineno, sep );
844                                 return 1;
845                         }
846                 
847                         if ( t ) {
848                                 *t = (time_t)val;
849         
850                         } else {
851                                 int     i;
852         
853                                 for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
854                                         tv[ i ] = (time_t)val;
855                                 }
856                         }
857                 }
858         
859         /* name to use as pseudo-root dn */
860         } else if ( strcasecmp( argv[ 0 ], "pseudorootdn" ) == 0 ) {
861                 int             i = mi->mi_ntargets - 1;
862                 struct berval   dn;
863
864                 if ( i < 0 ) {
865                         Debug( LDAP_DEBUG_ANY,
866         "%s: line %d: need \"uri\" directive first\n",
867                                 fname, lineno, 0 );
868                         return 1;
869                 }
870                 
871                 if ( argc != 2 ) {
872                         Debug( LDAP_DEBUG_ANY,
873         "%s: line %d: missing name in \"pseudorootdn <name>\" line\n",
874                                 fname, lineno, 0 );
875                         return 1;
876                 }
877
878                 dn.bv_val = argv[ 1 ];
879                 dn.bv_len = strlen( argv[ 1 ] );
880                 if ( dnNormalize( 0, NULL, NULL, &dn,
881                         &mi->mi_targets[ i ].mt_pseudorootdn, NULL ) != LDAP_SUCCESS )
882                 {
883                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
884                                         "pseudoroot DN '%s' is invalid\n",
885                                         fname, lineno, argv[ 1 ] );
886                         return( 1 );
887                 }
888
889         /* password to use as pseudo-root */
890         } else if ( strcasecmp( argv[ 0 ], "pseudorootpw" ) == 0 ) {
891                 int             i = mi->mi_ntargets - 1;
892
893                 if ( i < 0 ) {
894                         Debug( LDAP_DEBUG_ANY,
895         "%s: line %d: need \"uri\" directive first\n",
896                                 fname, lineno, 0 );
897                         return 1;
898                 }
899                 
900                 if ( argc != 2 ) {
901                         Debug( LDAP_DEBUG_ANY,
902         "%s: line %d: missing password in \"pseudorootpw <password>\" line\n",
903                             fname, lineno, 0 );
904                         return 1;
905                 }
906                 ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ].mt_pseudorootpw );
907         
908         /* dn massaging */
909         } else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
910                 BackendDB       *tmp_be;
911                 int             i = mi->mi_ntargets - 1, rc;
912                 struct berval   dn, nvnc, pvnc, nrnc, prnc;
913
914                 if ( i < 0 ) {
915                         Debug( LDAP_DEBUG_ANY,
916         "%s: line %d: need \"uri\" directive first\n",
917                                 fname, lineno, 0 );
918                         return 1;
919                 }
920                 
921                 /*
922                  * syntax:
923                  * 
924                  *      suffixmassage <suffix> <massaged suffix>
925                  *
926                  * the <suffix> field must be defined as a valid suffix
927                  * (or suffixAlias?) for the current database;
928                  * the <massaged suffix> shouldn't have already been
929                  * defined as a valid suffix or suffixAlias for the 
930                  * current server
931                  */
932                 if ( argc != 3 ) {
933                         Debug( LDAP_DEBUG_ANY,
934         "%s: line %d: syntax is \"suffixMassage <suffix> <massaged suffix>\"\n",
935                                 fname, lineno, 0 );
936                         return 1;
937                 }
938
939                 ber_str2bv( argv[ 1 ], 0, 0, &dn );
940                 if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
941                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
942                                         "suffix '%s' is invalid\n",
943                                         fname, lineno, argv[ 1 ] );
944                         return 1;
945                 }
946                 
947                 tmp_be = select_backend( &nvnc, 0, 0 );
948                 if ( tmp_be != NULL && tmp_be != be ) {
949                         Debug( LDAP_DEBUG_ANY, 
950         "%s: line %d: suffix already in use by another backend in"
951         " \"suffixMassage <suffix> <massaged suffix>\"\n",
952                                 fname, lineno, 0 );
953                         free( pvnc.bv_val );
954                         free( nvnc.bv_val );
955                         return 1;                                               
956                 }
957
958                 ber_str2bv( argv[ 2 ], 0, 0, &dn );
959                 if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
960                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
961                                 "massaged suffix '%s' is invalid\n",
962                                 fname, lineno, argv[ 2 ] );
963                         free( pvnc.bv_val );
964                         free( nvnc.bv_val );
965                         return 1;
966                 }
967         
968 #if 0   
969                 tmp_be = select_backend( &nrnc, 0, 0 );
970                 if ( tmp_be != NULL ) {
971                         Debug( LDAP_DEBUG_ANY,
972         "%s: line %d: massaged suffix already in use by another backend in" 
973         " \"suffixMassage <suffix> <massaged suffix>\"\n",
974                                 fname, lineno, 0 );
975                         free( pvnc.bv_val );
976                         free( nvnc.bv_val );
977                         free( prnc.bv_val );
978                         free( nrnc.bv_val );
979                         return 1;
980                 }
981 #endif
982                 
983                 /*
984                  * The suffix massaging is emulated by means of the
985                  * rewrite capabilities
986                  * FIXME: no extra rewrite capabilities should be added
987                  * to the database
988                  */
989                 rc = suffix_massage_config( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
990                                 &pvnc, &nvnc, &prnc, &nrnc );
991
992                 free( pvnc.bv_val );
993                 free( nvnc.bv_val );
994                 free( prnc.bv_val );
995                 free( nrnc.bv_val );
996
997                 return rc;
998                 
999         /* rewrite stuff ... */
1000         } else if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
1001                 int             i = mi->mi_ntargets - 1;
1002
1003                 if ( i < 0 ) {
1004                         Debug( LDAP_DEBUG_ANY, "%s: line %d: \"rewrite\" "
1005                                 "statement outside target definition.\n",
1006                                 fname, lineno, 0 );
1007                         return 1;
1008                 }
1009                 
1010                 return rewrite_parse( mi->mi_targets[ i ].mt_rwmap.rwm_rw,
1011                                 fname, lineno, argc, argv );
1012
1013         /* objectclass/attribute mapping */
1014         } else if ( strcasecmp( argv[ 0 ], "map" ) == 0 ) {
1015                 int             i = mi->mi_ntargets - 1;
1016
1017                 if ( i < 0 ) {
1018                         Debug( LDAP_DEBUG_ANY,
1019         "%s: line %d: need \"uri\" directive first\n",
1020                                 fname, lineno, 0 );
1021                         return 1;
1022                 }
1023
1024                 return ldap_back_map_config( &mi->mi_targets[ i ].mt_rwmap.rwm_oc, 
1025                                 &mi->mi_targets[ i ].mt_rwmap.rwm_at,
1026                                 fname, lineno, argc, argv );
1027
1028         } else if ( strcasecmp( argv[ 0 ], "nretries" ) == 0 ) {
1029                 int             i = mi->mi_ntargets - 1;
1030                 int             nretries = META_RETRY_UNDEFINED;
1031
1032                 if ( argc != 2 ) {
1033                         Debug( LDAP_DEBUG_ANY,
1034         "%s: line %d: need value in \"nretries <value>\"\n",
1035                                 fname, lineno, 0 );
1036                         return 1;
1037                 }
1038
1039                 if ( strcasecmp( argv[ 1 ], "forever" ) == 0 ) {
1040                         nretries = META_RETRY_FOREVER;
1041
1042                 } else if ( strcasecmp( argv[ 1 ], "never" ) == 0 ) {
1043                         nretries = META_RETRY_NEVER;
1044
1045                 } else {
1046                         if ( lutil_atoi( &nretries, argv[ 1 ] ) != 0 ) {
1047                                 Debug( LDAP_DEBUG_ANY,
1048         "%s: line %d: unable to parse value \"%s\" in \"nretries <value>\"\n",
1049                                         fname, lineno, argv[ 1 ] );
1050                                 return 1;
1051                         }
1052                 }
1053
1054                 if ( i < 0 ) {
1055                         mi->mi_nretries = nretries;
1056
1057                 } else {
1058                         mi->mi_targets[ i ].mt_nretries = nretries;
1059                 }
1060
1061         /* anything else */
1062         } else {
1063                 return SLAP_CONF_UNKNOWN;
1064         }
1065         return 0;
1066 }
1067
1068 int
1069 ldap_back_map_config(
1070                 struct ldapmap  *oc_map,
1071                 struct ldapmap  *at_map,
1072                 const char      *fname,
1073                 int             lineno,
1074                 int             argc,
1075                 char            **argv )
1076 {
1077         struct ldapmap          *map;
1078         struct ldapmapping      *mapping;
1079         char                    *src, *dst;
1080         int                     is_oc = 0;
1081
1082         if ( argc < 3 || argc > 4 ) {
1083                 Debug( LDAP_DEBUG_ANY,
1084         "%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
1085                         fname, lineno, 0 );
1086                 return 1;
1087         }
1088
1089         if ( strcasecmp( argv[ 1 ], "objectclass" ) == 0 ) {
1090                 map = oc_map;
1091                 is_oc = 1;
1092
1093         } else if ( strcasecmp( argv[ 1 ], "attribute" ) == 0 ) {
1094                 map = at_map;
1095
1096         } else {
1097                 Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is "
1098                         "\"map {objectclass | attribute} [<local> | *] "
1099                         "{<foreign> | *}\"\n",
1100                         fname, lineno, 0 );
1101                 return 1;
1102         }
1103
1104         if ( strcmp( argv[ 2 ], "*" ) == 0 ) {
1105                 if ( argc < 4 || strcmp( argv[ 3 ], "*" ) == 0 ) {
1106                         map->drop_missing = ( argc < 4 );
1107                         return 0;
1108                 }
1109                 src = dst = argv[ 3 ];
1110
1111         } else if ( argc < 4 ) {
1112                 src = "";
1113                 dst = argv[ 2 ];
1114
1115         } else {
1116                 src = argv[ 2 ];
1117                 dst = ( strcmp( argv[ 3 ], "*" ) == 0 ? src : argv[ 3 ] );
1118         }
1119
1120         if ( ( map == at_map )
1121                         && ( strcasecmp( src, "objectclass" ) == 0
1122                         || strcasecmp( dst, "objectclass" ) == 0 ) )
1123         {
1124                 Debug( LDAP_DEBUG_ANY,
1125                         "%s: line %d: objectclass attribute cannot be mapped\n",
1126                         fname, lineno, 0 );
1127         }
1128
1129         mapping = (struct ldapmapping *)ch_calloc( 2,
1130                 sizeof(struct ldapmapping) );
1131         if ( mapping == NULL ) {
1132                 Debug( LDAP_DEBUG_ANY,
1133                         "%s: line %d: out of memory\n",
1134                         fname, lineno, 0 );
1135                 return 1;
1136         }
1137         ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
1138         ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
1139         mapping[ 1 ].src = mapping[ 0 ].dst;
1140         mapping[ 1 ].dst = mapping[ 0 ].src;
1141
1142         /*
1143          * schema check
1144          */
1145         if ( is_oc ) {
1146                 if ( src[ 0 ] != '\0' ) {
1147                         if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
1148                                 Debug( LDAP_DEBUG_ANY,
1149         "%s: line %d: warning, source objectClass '%s' "
1150         "should be defined in schema\n",
1151                                         fname, lineno, src );
1152
1153                                 /*
1154                                  * FIXME: this should become an err
1155                                  */
1156                                 goto error_return;
1157                         }
1158                 }
1159
1160                 if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
1161                         Debug( LDAP_DEBUG_ANY,
1162         "%s: line %d: warning, destination objectClass '%s' "
1163         "is not defined in schema\n",
1164                                 fname, lineno, dst );
1165                 }
1166         } else {
1167                 int                     rc;
1168                 const char              *text = NULL;
1169                 AttributeDescription    *ad = NULL;
1170
1171                 if ( src[ 0 ] != '\0' ) {
1172                         rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
1173                         if ( rc != LDAP_SUCCESS ) {
1174                                 Debug( LDAP_DEBUG_ANY,
1175         "%s: line %d: warning, source attributeType '%s' "
1176         "should be defined in schema\n",
1177                                         fname, lineno, src );
1178
1179                                 /*
1180                                  * FIXME: this should become an err
1181                                  */
1182                                 /*
1183                                  * we create a fake "proxied" ad 
1184                                  * and add it here.
1185                                  */
1186
1187                                 rc = slap_bv2undef_ad( &mapping[ 0 ].src,
1188                                                 &ad, &text, SLAP_AD_PROXIED );
1189                                 if ( rc != LDAP_SUCCESS ) {
1190                                         char    buf[ SLAP_TEXT_BUFLEN ];
1191
1192                                         snprintf( buf, sizeof( buf ),
1193                                                 "source attributeType \"%s\": %d (%s)",
1194                                                 src, rc, text ? text : "" );
1195                                         Debug( LDAP_DEBUG_ANY,
1196                                                 "%s: line %d: %s\n",
1197                                                 fname, lineno, buf );
1198                                         goto error_return;
1199                                 }
1200                         }
1201
1202                         ad = NULL;
1203                 }
1204
1205                 rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
1206                 if ( rc != LDAP_SUCCESS ) {
1207                         Debug( LDAP_DEBUG_ANY,
1208         "%s: line %d: warning, destination attributeType '%s' "
1209         "is not defined in schema\n",
1210                                 fname, lineno, dst );
1211
1212                         /*
1213                          * we create a fake "proxied" ad 
1214                          * and add it here.
1215                          */
1216
1217                         rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
1218                                         &ad, &text, SLAP_AD_PROXIED );
1219                         if ( rc != LDAP_SUCCESS ) {
1220                                 char    buf[ SLAP_TEXT_BUFLEN ];
1221
1222                                 snprintf( buf, sizeof( buf ),
1223                                         "source attributeType \"%s\": %d (%s)\n",
1224                                         dst, rc, text ? text : "" );
1225                                 Debug( LDAP_DEBUG_ANY,
1226                                         "%s: line %d: %s\n",
1227                                         fname, lineno, buf );
1228                                 return 1;
1229                         }
1230                 }
1231         }
1232
1233         if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
1234                         || avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
1235         {
1236                 Debug( LDAP_DEBUG_ANY,
1237                         "%s: line %d: duplicate mapping found.\n",
1238                         fname, lineno, 0 );
1239                 goto error_return;
1240         }
1241
1242         if ( src[ 0 ] != '\0' ) {
1243                 avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
1244                                         mapping_cmp, mapping_dup );
1245         }
1246         avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
1247                                 mapping_cmp, mapping_dup );
1248
1249         return 0;
1250
1251 error_return:;
1252         if ( mapping ) {
1253                 ch_free( mapping[ 0 ].src.bv_val );
1254                 ch_free( mapping[ 0 ].dst.bv_val );
1255                 ch_free( mapping );
1256         }
1257
1258         return 1;
1259 }
1260
1261
1262 #ifdef ENABLE_REWRITE
1263 static char *
1264 suffix_massage_regexize( const char *s )
1265 {
1266         char *res, *ptr;
1267         const char *p, *r;
1268         int i;
1269
1270         if ( s[ 0 ] == '\0' ) {
1271                 return ch_strdup( "^(.+)$" );
1272         }
1273
1274         for ( i = 0, p = s; 
1275                         ( r = strchr( p, ',' ) ) != NULL; 
1276                         p = r + 1, i++ )
1277                 ;
1278
1279         res = ch_calloc( sizeof( char ),
1280                         strlen( s )
1281                         + STRLENOF( "((.+),)?" )
1282                         + STRLENOF( "[ ]?" ) * i
1283                         + STRLENOF( "$" ) + 1 );
1284
1285         ptr = lutil_strcopy( res, "((.+),)?" );
1286         for ( i = 0, p = s;
1287                         ( r = strchr( p, ',' ) ) != NULL;
1288                         p = r + 1 , i++ ) {
1289                 ptr = lutil_strncopy( ptr, p, r - p + 1 );
1290                 ptr = lutil_strcopy( ptr, "[ ]?" );
1291
1292                 if ( r[ 1 ] == ' ' ) {
1293                         r++;
1294                 }
1295         }
1296         ptr = lutil_strcopy( ptr, p );
1297         ptr[ 0 ] = '$';
1298         ptr++;
1299         ptr[ 0 ] = '\0';
1300
1301         return res;
1302 }
1303
1304 static char *
1305 suffix_massage_patternize( const char *s, const char *p )
1306 {
1307         ber_len_t       len;
1308         char            *res, *ptr;
1309
1310         len = strlen( p );
1311
1312         if ( s[ 0 ] == '\0' ) {
1313                 len++;
1314         }
1315
1316         res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
1317         if ( res == NULL ) {
1318                 return NULL;
1319         }
1320
1321         ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
1322         if ( s[ 0 ] == '\0' ) {
1323                 ptr[ 0 ] = ',';
1324                 ptr++;
1325         }
1326         lutil_strcopy( ptr, p );
1327
1328         return res;
1329 }
1330
1331 int
1332 suffix_massage_config( 
1333                 struct rewrite_info *info,
1334                 struct berval *pvnc,
1335                 struct berval *nvnc,
1336                 struct berval *prnc,
1337                 struct berval *nrnc
1338 )
1339 {
1340         char *rargv[ 5 ];
1341         int line = 0;
1342
1343         rargv[ 0 ] = "rewriteEngine";
1344         rargv[ 1 ] = "on";
1345         rargv[ 2 ] = NULL;
1346         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1347
1348         rargv[ 0 ] = "rewriteContext";
1349         rargv[ 1 ] = "default";
1350         rargv[ 2 ] = NULL;
1351         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1352
1353         rargv[ 0 ] = "rewriteRule";
1354         rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
1355         rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
1356         rargv[ 3 ] = ":";
1357         rargv[ 4 ] = NULL;
1358         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1359         ch_free( rargv[ 1 ] );
1360         ch_free( rargv[ 2 ] );
1361
1362         if ( BER_BVISEMPTY( pvnc ) ) {
1363                 rargv[ 0 ] = "rewriteRule";
1364                 rargv[ 1 ] = "^$";
1365                 rargv[ 2 ] = prnc->bv_val;
1366                 rargv[ 3 ] = ":";
1367                 rargv[ 4 ] = NULL;
1368                 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1369         }
1370         
1371         rargv[ 0 ] = "rewriteContext";
1372         rargv[ 1 ] = "searchEntryDN";
1373         rargv[ 2 ] = NULL;
1374         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1375
1376         rargv[ 0 ] = "rewriteRule";
1377         rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
1378         rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
1379         rargv[ 3 ] = ":";
1380         rargv[ 4 ] = NULL;
1381         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1382         ch_free( rargv[ 1 ] );
1383         ch_free( rargv[ 2 ] );
1384
1385         if ( BER_BVISEMPTY( prnc ) ) {
1386                 rargv[ 0 ] = "rewriteRule";
1387                 rargv[ 1 ] = "^$";
1388                 rargv[ 2 ] = pvnc->bv_val;
1389                 rargv[ 3 ] = ":";
1390                 rargv[ 4 ] = NULL;
1391                 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1392         }
1393         
1394         /* backward compatibility */
1395         rargv[ 0 ] = "rewriteContext";
1396         rargv[ 1 ] = "searchResult";
1397         rargv[ 2 ] = "alias";
1398         rargv[ 3 ] = "searchEntryDN";
1399         rargv[ 4 ] = NULL;
1400         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1401         
1402         rargv[ 0 ] = "rewriteContext";
1403         rargv[ 1 ] = "matchedDN";
1404         rargv[ 2 ] = "alias";
1405         rargv[ 3 ] = "searchEntryDN";
1406         rargv[ 4 ] = NULL;
1407         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1408
1409         rargv[ 0 ] = "rewriteContext";
1410         rargv[ 1 ] = "searchAttrDN";
1411         rargv[ 2 ] = "alias";
1412         rargv[ 3 ] = "searchEntryDN";
1413         rargv[ 4 ] = NULL;
1414         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
1415
1416         /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
1417          * see servers/slapd/overlays/rwm.h for details */
1418         rargv[ 0 ] = "rewriteContext";
1419         rargv[ 1 ] = "referralAttrDN";
1420         rargv[ 2 ] = NULL;
1421         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1422
1423         rargv[ 0 ] = "rewriteContext";
1424         rargv[ 1 ] = "referralDN";
1425         rargv[ 2 ] = NULL;
1426         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
1427         
1428         return 0;
1429 }
1430 #endif /* ENABLE_REWRITE */
1431