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