]> git.sur5r.net Git - openldap/blob - servers/slapd/dn.c
add new ber dump routine (behind NEW_LOGGING)
[openldap] / servers / slapd / dn.c
1 /* dn.c - routines for dealing with distinguished names */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/socket.h>
14 #include <ac/string.h>
15 #include <ac/time.h>
16
17 #include "ldap_pvt.h"
18
19 #include "slap.h"
20
21 #define B4LEADTYPE              0
22 #define B4TYPE                  1
23 #define INOIDTYPE               2
24 #define INKEYTYPE               3
25 #define B4EQUAL                 4
26 #define B4VALUE                 5
27 #define INVALUE                 6
28 #define INQUOTEDVALUE   7
29 #define B4SEPARATOR             8
30
31 /*
32  * dn_validate - validate and compress dn.  the dn is
33  * compressed in place are returned if valid.
34  */
35
36 char *
37 dn_validate( char *dn_in )
38 {
39         char    *d, *s;
40         int     state, gotesc;
41         char    *dn = dn_in;
42
43         gotesc = 0;
44         state = B4LEADTYPE;
45         for ( d = s = dn; *s; s++ ) {
46                 switch ( state ) {
47                 case B4LEADTYPE:
48                 case B4TYPE:
49                         if ( OID_LEADCHAR(*s) ) {
50                                 state = INOIDTYPE;
51                                 *d++ = *s;
52                         } else if ( ATTR_LEADCHAR(*s) ) {
53                                 state = INKEYTYPE;
54                                 *d++ = *s;
55                         } else if ( ! ASCII_SPACE( *s ) ) {
56                                 dn = NULL;
57                                 state = INKEYTYPE;
58                                 *d++ = *s;
59                         }
60                         break;
61
62                 case INOIDTYPE:
63                         if ( OID_CHAR(*s) ) {
64                                 *d++ = *s;
65                         } else if ( *s == '=' ) {
66                                 state = B4VALUE;
67                                 *d++ = *s;
68                         } else if ( ASCII_SPACE( *s ) ) {
69                                 state = B4EQUAL;
70                         } else {
71                                 dn = NULL;
72                                 *d++ = *s;
73                         }
74                         break;
75
76                 case INKEYTYPE:
77                         if ( ATTR_CHAR(*s) ) {
78                                 *d++ = *s;
79                         } else if ( *s == '=' ) {
80                                 state = B4VALUE;
81                                 *d++ = *s;
82                         } else if ( ASCII_SPACE( *s ) ) {
83                                 state = B4EQUAL;
84                         } else {
85                                 dn = NULL;
86                                 *d++ = *s;
87                         }
88                         break;
89
90                 case B4EQUAL:
91                         if ( *s == '=' ) {
92                                 state = B4VALUE;
93                                 *d++ = *s;
94                         } else if ( ! ASCII_SPACE( *s ) ) {
95                                 /* not a valid dn - but what can we do here? */
96                                 *d++ = *s;
97                                 dn = NULL;
98                         }
99                         break;
100
101                 case B4VALUE:
102                         if ( *s == '"' ) {
103                                 state = INQUOTEDVALUE;
104                                 *d++ = *s;
105                         } else if ( ! ASCII_SPACE( *s ) ) { 
106                                 state = INVALUE;
107                                 *d++ = *s;
108                         }
109                         break;
110
111                 case INVALUE:
112                         if ( !gotesc && RDN_SEPARATOR( *s ) ) {
113                                 while ( ASCII_SPACE( *(d - 1) ) )
114                                         d--;
115                                 state = B4TYPE;
116                                 if ( *s == '+' ) {
117                                         *d++ = *s;
118                                 } else {
119                                         *d++ = ',';
120                                 }
121                         } else if ( gotesc && !RDN_NEEDSESCAPE( *s ) &&
122                             !RDN_SEPARATOR( *s ) ) {
123                                 *--d = *s;
124                                 d++;
125                         } else if( !ASCII_SPACE( *s ) || !ASCII_SPACE( *(d - 1) ) ) {
126                                 *d++ = *s;
127                         }
128                         break;
129
130                 case INQUOTEDVALUE:
131                         if ( !gotesc && *s == '"' ) {
132                                 state = B4SEPARATOR;
133                                 *d++ = *s;
134                         } else if ( gotesc && !RDN_NEEDSESCAPE( *s ) ) {
135                                 *--d = *s;
136                                 d++;
137                         } else if( !ASCII_SPACE( *s ) || !ASCII_SPACE( *(d - 1) ) ) {
138                                 *d++ = *s;
139                         }
140                         break;
141
142                 case B4SEPARATOR:
143                         if ( RDN_SEPARATOR( *s ) ) {
144                                 state = B4TYPE;
145                                 *d++ = *s;
146                         } else if ( !ASCII_SPACE( *s ) ) {
147                                 dn = NULL;
148                         }
149                         break;
150
151                 default:
152                         dn = NULL;
153                         Debug( LDAP_DEBUG_ANY,
154                             "dn_validate - unknown state %d\n", state, 0, 0 );
155                         break;
156                 }
157
158                 if ( *s == '\\' ) {
159                         gotesc = 1;
160                 } else {
161                         gotesc = 0;
162                 }
163         }
164
165         /* trim trailing spaces */
166         while( d > dn_in && ASCII_SPACE( *(d-1) ) ) {
167                 --d;
168         }
169         *d = '\0';
170
171         if( gotesc ) {
172                 /* shouldn't be left in escape */
173                 dn = NULL;
174         }
175
176         /* check end state */
177         switch( state ) {
178         case B4LEADTYPE:        /* looking for first type */
179         case B4SEPARATOR:       /* looking for separator */
180         case INVALUE:           /* inside value */
181                 break;
182         default:
183                 dn = NULL;
184         }
185
186         return( dn );
187 }
188
189 /*
190  * dn_normalize - put dn into a canonical form suitable for storing
191  * in a hash database.  this involves normalizing the case as well as
192  * the format.  the dn is normalized in place as well as returned if valid.
193  */
194
195 char *
196 dn_normalize( char *dn )
197 {
198         /* upper case it */
199         ldap_pvt_str2upper( dn );
200
201         /* validate and compress dn */
202         dn = dn_validate( dn );
203
204         return( dn );
205 }
206
207 /*
208  * dn_parent - return a copy of the dn of dn's parent
209  */
210
211 char *
212 dn_parent(
213     Backend     *be,
214     const char  *dn
215 )
216 {
217         const char      *s;
218         int     inquote;
219
220         if( dn == NULL ) {
221                 return NULL;
222         }
223
224         while(*dn != '\0' && ASCII_SPACE(*dn)) {
225                 dn++;
226         }
227
228         if( *dn == '\0' ) {
229                 return NULL;
230         }
231
232         if ( be != NULL && be_issuffix( be, dn ) ) {
233                 return NULL;
234         }
235
236         /*
237          * assume it is an X.500-style name, which looks like
238          * foo=bar,sha=baz,...
239          */
240
241         inquote = 0;
242         for ( s = dn; *s; s++ ) {
243                 if ( *s == '\\' ) {
244                         if ( *(s + 1) ) {
245                                 s++;
246                         }
247                         continue;
248                 }
249                 if ( inquote ) {
250                         if ( *s == '"' ) {
251                                 inquote = 0;
252                         }
253                 } else {
254                         if ( *s == '"' ) {
255                                 inquote = 1;
256                         } else if ( DN_SEPARATOR( *s ) ) {
257                                 return ch_strdup( &s[1] );
258                         }
259                 }
260         }
261
262         return ch_strdup( "" );
263 }
264
265 char * dn_rdn( 
266     Backend     *be,
267     const char  *dn_in )
268 {
269         char    *dn, *s;
270         int     inquote;
271
272         if( dn_in == NULL ) {
273                 return NULL;
274         }
275
276         while(*dn_in && ASCII_SPACE(*dn_in)) {
277                 dn_in++;
278         }
279
280         if( *dn_in == '\0' ) {
281                 return( NULL );
282         }
283
284         if ( be != NULL && be_issuffix( be, dn_in ) ) {
285                 return( NULL );
286         }
287
288         dn = ch_strdup( dn_in );
289
290         inquote = 0;
291
292         for ( s = dn; *s; s++ ) {
293                 if ( *s == '\\' ) {
294                         if ( *(s + 1) ) {
295                                 s++;
296                         }
297                         continue;
298                 }
299                 if ( inquote ) {
300                         if ( *s == '"' ) {
301                                 inquote = 0;
302                         }
303                 } else {
304                         if ( *s == '"' ) {
305                                 inquote = 1;
306                         } else if ( DN_SEPARATOR( *s ) ) {
307                                 *s = '\0';
308                                 return( dn );
309                         }
310                 }
311         }
312
313         return( dn );
314 }
315
316
317 /*
318  * return a charray of all subtrees to which the DN resides in
319  */
320 char **dn_subtree(
321         Backend *be,
322     const char  *dn )
323 {
324         char *child, *parent;
325         char **subtree = NULL;
326         
327         child = ch_strdup( dn );
328
329         do {
330                 charray_add( &subtree, child );
331
332                 parent = dn_parent( be, child );
333
334                 free( child );
335
336                 child = parent;
337         } while ( child != NULL );
338
339         return subtree;
340 }
341
342
343 /*
344  * dn_issuffix - tells whether suffix is a suffix of dn.  both dn
345  * and suffix must be normalized.
346  */
347
348 int
349 dn_issuffix(
350     const char  *dn,
351     const char  *suffix
352 )
353 {
354         int     dnlen, suffixlen;
355
356         if ( dn == NULL ) {
357                 return( 0 );
358         }
359
360         suffixlen = strlen( suffix );
361         dnlen = strlen( dn );
362
363         if ( suffixlen > dnlen ) {
364                 return( 0 );
365         }
366
367         return( strcmp( dn + dnlen - suffixlen, suffix ) == 0 );
368 }
369
370 /*
371  * get_next_substring(), rdn_attr_type(), rdn_attr_value(), and
372  * build_new_dn().
373  * 
374  * Copyright 1999, Juan C. Gomez, All rights reserved.
375  * This software is not subject to any license of Silicon Graphics 
376  * Inc. or Purdue University.
377  *
378  * Redistribution and use in source and binary forms are permitted
379  * without restriction or fee of any kind as long as this notice
380  * is preserved.
381  *
382  */
383
384 /* get_next_substring:
385  *
386  * Gets next substring in s, using d (or the end of the string '\0') as a 
387  * string delimiter, and places it in a duplicated memory space. Leading 
388  * spaces are ignored. String s **must** be null-terminated.
389  */ 
390
391 static char * 
392 get_next_substring( const char * s, char d )
393 {
394
395         char    *str, *r;
396
397         r = str = ch_malloc( strlen(s) + 1 );
398
399         /* Skip leading spaces */
400         
401         while ( *s && ASCII_SPACE(*s) ) {
402                 s++;
403         }
404         
405         /* Copy word */
406
407         while ( *s && (*s != d) ) {
408
409                 /* Don't stop when you see trailing spaces may be a multi-word
410                 * string, i.e. name=John Doe!
411                 */
412
413                 *str++ = *s++;
414             
415         }
416         
417         *str = '\0';
418         
419         return r;
420         
421 }
422
423
424 /* rdn_attr_type:
425  *
426  * Given a string (i.e. an rdn) of the form:
427  *       "attribute_type = attribute_value"
428  * this function returns the type of an attribute, that is the 
429  * string "attribute_type" which is placed in newly allocated 
430  * memory. The returned string will be null-terminated.
431  */
432
433 char * rdn_attr_type( const char * s )
434 {
435         return get_next_substring( s, '=' );
436 }
437
438
439 /* rdn_attr_value:
440  *
441  * Given a string (i.e. an rdn) of the form:
442  *       "attribute_type = attribute_value"
443  * this function returns "attribute_type" which is placed in newly allocated 
444  * memory. The returned string will be null-terminated and may contain 
445  * spaces (i.e. "John Doe\0").
446  */
447
448 char * 
449 rdn_attr_value( const char * rdn )
450 {
451
452         const char      *str;
453
454         if ( (str = strchr( rdn, '=' )) != NULL ) {
455                 return get_next_substring(++str, '\0');
456         }
457
458         return NULL;
459
460 }
461
462
463 int rdn_validate( const char * rdn )
464 {
465         /* just a simple check for now */
466         return strchr( rdn, '=' ) != NULL;
467 }
468
469
470 /* build_new_dn:
471  *
472  * Used by ldbm/bdb2_back_modrdn to create the new dn of entries being
473  * renamed.
474  *
475  * new_dn = parent (p_dn)  + separator(s) + rdn (newrdn) + null.
476  */
477
478 void
479 build_new_dn( char ** new_dn,
480         const char *e_dn,
481         const char * p_dn,
482         const char * newrdn )
483 {
484
485     if ( p_dn == NULL ) {
486         *new_dn = ch_strdup( newrdn );
487         return;
488     }
489     
490     *new_dn = (char *) ch_malloc( strlen( p_dn ) + strlen( newrdn ) + 3 );
491
492         strcpy( *new_dn, newrdn );
493         strcat( *new_dn, "," );
494         strcat( *new_dn, p_dn );
495 }