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