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