]> git.sur5r.net Git - openldap/blob - servers/slapd/dn.c
Modified to use libtool's ltdl instead of gmodule
[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  * dn_issuffix - tells whether suffix is a suffix of dn.  both dn
344  * and suffix must be normalized.
345  */
346
347 int
348 dn_issuffix(
349     char        *dn,
350     char        *suffix
351 )
352 {
353         int     dnlen, suffixlen;
354
355         if ( dn == NULL ) {
356                 return( 0 );
357         }
358
359         suffixlen = strlen( suffix );
360         dnlen = strlen( dn );
361
362         if ( suffixlen > dnlen ) {
363                 return( 0 );
364         }
365
366         return( strcmp( dn + dnlen - suffixlen, suffix ) == 0 );
367 }
368
369 #ifdef DNS_DN
370 /*
371  * dn_type - tells whether the given dn is an X.500 thing or DNS thing
372  * returns (defined in slap.h): DN_DNS          dns-style thing
373  *                              DN_X500         x500-style thing
374  */
375
376 int
377 dn_type( char *dn )
378 {
379         return( strchr( dn, '=' ) == NULL ? DN_DNS : DN_X500 );
380 }
381 #endif
382
383 char *
384 str2upper( char *str )
385 {
386         char    *s;
387
388         /* normalize case */
389         for ( s = str; *s; s++ ) {
390                 *s = TOUPPER( (unsigned char) *s );
391         }
392
393         return( str );
394 }
395
396 char *
397 str2lower( char *str )
398 {
399         char    *s;
400
401         /* normalize case */
402         for ( s = str; *s; s++ ) {
403                 *s = TOLOWER( (unsigned char) *s );
404         }
405
406         return( str );
407 }
408
409
410 /*
411  * get_next_substring(), rdn_attr_type(), rdn_attr_value(), and
412  * build_new_dn().
413  * 
414  * Copyright 1999, Juan C. Gomez, All rights reserved.
415  * This software is not subject to any license of Silicon Graphics 
416  * Inc. or Purdue University.
417  *
418  * Redistribution and use in source and binary forms are permitted
419  * without restriction or fee of any kind as long as this notice
420  * is preserved.
421  *
422  */
423
424 /* get_next_substring:
425  *
426  * Gets next substring in s, using d (or the end of the string '\0') as a 
427  * string delimiter, and places it in a duplicated memory space. Leading 
428  * spaces are ignored. String s **must** be null-terminated.
429  */ 
430
431 static char * 
432 get_next_substring( char * s, char d )
433 {
434
435         char    *str, *r;
436
437         r = str = ch_malloc( strlen(s) + 1 );
438
439         /* Skip leading spaces */
440         
441         while ( *s && SPACE(*s) ) {
442             
443                 s++;
444             
445         }
446         
447         /* Copy word */
448
449         while ( *s && (*s != d) ) {
450
451                 /* Don't stop when you see trailing spaces may be a multi-word
452                 * string, i.e. name=John Doe!
453                 */
454
455                 *str++ = *s++;
456             
457         }
458         
459         *str = '\0';
460         
461         return r;
462         
463 }
464
465
466 /* rdn_attr_type:
467  *
468  * Given a string (i.e. an rdn) of the form:
469  *       "attribute_type = attribute_value"
470  * this function returns the type of an attribute, that is the 
471  * string "attribute_type" which is placed in newly allocated 
472  * memory. The returned string will be null-terminated.
473  */
474
475 char * rdn_attr_type( char * s )
476 {
477
478         return get_next_substring( s, '=' );
479
480 }
481
482
483 /* rdn_attr_value:
484  *
485  * Given a string (i.e. an rdn) of the form:
486  *       "attribute_type = attribute_value"
487  * this function returns "attribute_type" which is placed in newly allocated 
488  * memory. The returned string will be null-terminated and may contain 
489  * spaces (i.e. "John Doe\0").
490  */
491
492 char * 
493 rdn_attr_value( char * rdn )
494 {
495
496         char    *str;
497
498         if ( (str = strchr( rdn, '=' )) != NULL ) {
499
500                 return get_next_substring(++str, '\0');
501
502         }
503
504         return NULL;
505
506 }
507
508
509 int rdn_validate( const char * rdn )
510 {
511         /* just a simple check for now */
512         return strchr( rdn, '=' ) != NULL;
513 }
514
515
516 /* build_new_dn:
517  *
518  * Used by ldbm/bdb2_back_modrdn to create the new dn of entries being
519  * renamed.
520  *
521  * new_dn = parent (p_dn)  + separator(s) + rdn (newrdn) + null.
522  */
523
524 void
525 build_new_dn( char ** new_dn, char *e_dn, char * p_dn, char * newrdn )
526 {
527
528     if ( p_dn == NULL ) {
529
530         *new_dn = ch_strdup( newrdn );
531         return;
532
533     }
534     
535     *new_dn = (char *) ch_malloc( strlen( p_dn ) + strlen( newrdn ) + 3 );
536
537 #ifdef DNS_DN
538     if ( dn_type( e_dn ) == DN_X500 ) {
539 #endif
540
541         strcpy( *new_dn, newrdn );
542         strcat( *new_dn, "," );
543         strcat( *new_dn, p_dn );
544
545 #ifdef DNS_DN
546     } else {
547
548         char    *s;
549         char    sep[2];
550
551         strcpy( *new_dn, newrdn );
552         s = strchr( newrdn, '\0' );
553         s--;
554
555         if ( (*s != '.') && (*s != '@') ) {
556
557             if ( (s = strpbrk( e_dn, ".@" )) != NULL ) {
558
559                 sep[0] = *s;
560                 sep[1] = '\0';
561                 strcat( *new_dn, sep );
562
563             }
564
565         }
566
567         strcat( *new_dn, p_dn );
568
569     }
570 #endif
571     
572 }