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