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