1 /*
2 * Copyright (c) 2001 by Sun Microsystems, Inc.
3 * All rights reserved.
4 */
5
6 /*
7 * The contents of this file are subject to the Netscape Public
8 * License Version 1.1 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.mozilla.org/NPL/
11 *
12 * Software distributed under the License is distributed on an "AS
13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is Netscape
21 * Communications Corporation. Portions created by Netscape are
22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23 * Rights Reserved.
24 *
25 * Contributor(s):
26 */
27 /*
28 * Copyright (c) 1994 Regents of the University of Michigan.
29 * All rights reserved.
30 */
31 /*
32 * getdn.c
33 */
34
35 #include "ldap-int.h"
36
37 char *
38 LDAP_CALL
ldap_get_dn(LDAP * ld,LDAPMessage * entry)39 ldap_get_dn( LDAP *ld, LDAPMessage *entry )
40 {
41 char *dn;
42 struct berelement tmp;
43
44 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
45
46 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
47 return( NULL ); /* punt */
48 }
49
50 if ( !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) {
51 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
52 return( NULL );
53 }
54
55 tmp = *entry->lm_ber; /* struct copy */
56 if ( ber_scanf( &tmp, "{a", &dn ) == LBER_ERROR ) {
57 LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
58 return( NULL );
59 }
60
61 return( dn );
62 }
63
64 char *
65 LDAP_CALL
ldap_dn2ufn(const char * dn)66 ldap_dn2ufn( const char *dn )
67 {
68 char *p, *ufn, *r;
69 size_t plen;
70 int state;
71
72 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
73
74 if ( dn == NULL ) {
75 dn = "";
76 }
77
78 if ( ldap_is_dns_dn( dn ) || ( p = strchr( dn, '=' )) == NULL )
79 return( nsldapi_strdup( (char *)dn ));
80
81 ufn = nsldapi_strdup( ++p );
82
83 #define INQUOTE 1
84 #define OUTQUOTE 2
85 state = OUTQUOTE;
86 for ( p = ufn, r = ufn; *p; p += plen ) {
87 plen = 1;
88 switch ( *p ) {
89 case '\\':
90 if ( *++p == '\0' )
91 plen=0;
92 else {
93 *r++ = '\\';
94 r += (plen = LDAP_UTF8COPY(r,p));
95 }
96 break;
97 case '"':
98 if ( state == INQUOTE )
99 state = OUTQUOTE;
100 else
101 state = INQUOTE;
102 *r++ = *p;
103 break;
104 case ';':
105 case ',':
106 if ( state == OUTQUOTE )
107 *r++ = ',';
108 else
109 *r++ = *p;
110 break;
111 case '=':
112 if ( state == INQUOTE )
113 *r++ = *p;
114 else {
115 char *rsave = r;
116 LDAP_UTF8DEC(r);
117 *rsave = '\0';
118 while ( !ldap_utf8isspace( r ) && *r != ';'
119 && *r != ',' && r > ufn )
120 LDAP_UTF8DEC(r);
121 LDAP_UTF8INC(r);
122
123 if ( strcasecmp( r, "c" )
124 && strcasecmp( r, "o" )
125 && strcasecmp( r, "ou" )
126 && strcasecmp( r, "st" )
127 && strcasecmp( r, "l" )
128 && strcasecmp( r, "dc" )
129 && strcasecmp( r, "uid" )
130 && strcasecmp( r, "cn" ) ) {
131 r = rsave;
132 *r++ = '=';
133 }
134 }
135 break;
136 default:
137 r += (plen = LDAP_UTF8COPY(r,p));
138 break;
139 }
140 }
141 *r = '\0';
142
143 return( ufn );
144 }
145
146 char **
147 LDAP_CALL
ldap_explode_dns(const char * dn)148 ldap_explode_dns( const char *dn )
149 {
150 int ncomps, maxcomps;
151 char *s, *cpydn;
152 char **rdns;
153 #ifdef HAVE_STRTOK_R /* defined in portable.h */
154 char *lasts;
155 #endif
156
157 if ( dn == NULL ) {
158 dn = "";
159 }
160
161 if ( (rdns = (char **)NSLDAPI_MALLOC( 8 * sizeof(char *) )) == NULL ) {
162 return( NULL );
163 }
164
165 maxcomps = 8;
166 ncomps = 0;
167 cpydn = nsldapi_strdup( (char *)dn );
168 for ( s = STRTOK( cpydn, "@.", &lasts ); s != NULL;
169 s = STRTOK( NULL, "@.", &lasts ) ) {
170 if ( ncomps == maxcomps ) {
171 maxcomps *= 2;
172 if ( (rdns = (char **)NSLDAPI_REALLOC( rdns, maxcomps *
173 sizeof(char *) )) == NULL ) {
174 NSLDAPI_FREE( cpydn );
175 return( NULL );
176 }
177 }
178 rdns[ncomps++] = nsldapi_strdup( s );
179 }
180 rdns[ncomps] = NULL;
181 NSLDAPI_FREE( cpydn );
182
183 return( rdns );
184 }
185
186 #define LDAP_DN 1
187 #define LDAP_RDN 2
188
189 static char **
ldap_explode(const char * dn,const int notypes,const int nametype)190 ldap_explode( const char *dn, const int notypes, const int nametype )
191 {
192 char *p, *q, *rdnstart, **rdns = NULL;
193 size_t plen = 0;
194 int state, count = 0, endquote, len, goteq;
195
196 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_explode\n", 0, 0, 0 );
197
198 if ( dn == NULL ) {
199 dn = "";
200 }
201
202 #if 0
203 if ( ldap_is_dns_dn( dn ) ) {
204 return( ldap_explode_dns( dn ) );
205 }
206 #endif
207
208 while ( ldap_utf8isspace( (char *)dn )) { /* ignore leading spaces */
209 ++dn;
210 }
211
212 p = rdnstart = (char *) dn;
213 state = OUTQUOTE;
214 goteq = 0;
215
216 do {
217 p += plen;
218 plen = 1;
219 switch ( *p ) {
220 case '\\':
221 if ( *++p == '\0' )
222 p--;
223 else
224 plen = LDAP_UTF8LEN(p);
225 break;
226 case '"':
227 if ( state == INQUOTE )
228 state = OUTQUOTE;
229 else
230 state = INQUOTE;
231 break;
232 case '+': if ( nametype != LDAP_RDN ) break;
233 /* FALLTHROUGH */
234 case ';':
235 case ',':
236 case '\0':
237 if ( state == OUTQUOTE ) {
238 /*
239 * semicolon and comma are not valid RDN
240 * separators.
241 */
242 if ( nametype == LDAP_RDN &&
243 ( *p == ';' || *p == ',' || !goteq)) {
244 ldap_charray_free( rdns );
245 return NULL;
246 }
247 if ( (*p == ',' || *p == ';') && !goteq ) {
248 /* If we get here, we have a case similar
249 * to <attr>=<value>,<string>,<attr>=<value>
250 * This is not a valid dn */
251 ldap_charray_free( rdns );
252 return NULL;
253 }
254 goteq = 0;
255 ++count;
256 if ( rdns == NULL ) {
257 if (( rdns = (char **)NSLDAPI_MALLOC( 8
258 * sizeof( char *))) == NULL )
259 return( NULL );
260 } else if ( count >= 8 ) {
261 if (( rdns = (char **)NSLDAPI_REALLOC(
262 rdns, (count+1) *
263 sizeof( char *))) == NULL )
264 return( NULL );
265 }
266 rdns[ count ] = NULL;
267 endquote = 0;
268 if ( notypes ) {
269 for ( q = rdnstart;
270 q < p && *q != '='; ++q ) {
271 ;
272 }
273 if ( q < p ) { /* *q == '=' */
274 rdnstart = ++q;
275 }
276 if ( *rdnstart == '"' ) {
277 ++rdnstart;
278 }
279
280 if ( *(p-1) == '"' ) {
281 endquote = 1;
282 --p;
283 }
284 }
285
286 len = p - rdnstart;
287 if (( rdns[ count-1 ] = (char *)NSLDAPI_CALLOC(
288 1, len + 1 )) != NULL ) {
289 SAFEMEMCPY( rdns[ count-1 ], rdnstart,
290 len );
291 if ( !endquote ) {
292 /* trim trailing spaces */
293 while ( len > 0 &&
294 ldap_utf8isspace(
295 &rdns[count-1][len-1] )) {
296 --len;
297 }
298 }
299 rdns[ count-1 ][ len ] = '\0';
300 }
301
302 /*
303 * Don't forget to increment 'p' back to where
304 * it should be. If we don't, then we will
305 * never get past an "end quote."
306 */
307 if ( endquote == 1 )
308 p++;
309
310 rdnstart = *p ? p + 1 : p;
311 while ( ldap_utf8isspace( rdnstart ))
312 ++rdnstart;
313 }
314 break;
315 case '=':
316 if ( state == OUTQUOTE ) {
317 goteq = 1;
318 }
319 /* FALLTHROUGH */
320 default:
321 plen = LDAP_UTF8LEN(p);
322 break;
323 }
324 } while ( *p );
325
326 return( rdns );
327 }
328
329 char **
330 LDAP_CALL
ldap_explode_dn(const char * dn,const int notypes)331 ldap_explode_dn( const char *dn, const int notypes )
332 {
333 return( ldap_explode( dn, notypes, LDAP_DN ) );
334 }
335
336 char **
337 LDAP_CALL
ldap_explode_rdn(const char * rdn,const int notypes)338 ldap_explode_rdn( const char *rdn, const int notypes )
339 {
340 return( ldap_explode( rdn, notypes, LDAP_RDN ) );
341 }
342
343 int
344 LDAP_CALL
ldap_is_dns_dn(const char * dn)345 ldap_is_dns_dn( const char *dn )
346 {
347 return( dn != NULL && dn[ 0 ] != '\0' && strchr( dn, '=' ) == NULL &&
348 strchr( dn, ',' ) == NULL );
349 }
350
351 #ifdef _SOLARIS_SDK
352
353 /*
354 * Convert a DNS domain name into an X.500 distinguished name.
355 * For example, "sales.wiz.com" -> "dc=sales,dc=wiz,dc=com"
356 *
357 * If an error is encountered zero is returned, otherwise a string
358 * distinguished name and the number of nameparts is returned.
359 * The caller should free the returned string if it is non-zero.
360 */
361
362 char *
ldap_dns_to_dn(char * dns_name,int * nameparts)363 ldap_dns_to_dn(
364 char *dns_name,
365 int *nameparts
366 )
367 {
368 size_t dns_len;
369 char *dn = 0;
370 char *cp;
371
372 /* check for NULL string, empty name and name ending in '.' */
373 if (dns_name && (dns_len = strlen(dns_name)) &&
374 (dns_name[dns_len - 1] != '.')) {
375 if (dn = (char *)malloc(dns_len * 3 + 1)) {
376 *nameparts = 0;
377 cp = dn;
378 while (*dns_name) {
379 *cp++ = 'd';
380 *cp++ = 'c';
381 *cp++ = '=';
382
383 while (*dns_name && (*dns_name != '.')) {
384 *cp++ = *dns_name++;
385 }
386 if (*dns_name == '.') {
387 dns_name++;
388 *cp++ = ',';
389 }
390 (*nameparts)++;
391 }
392 *cp = '\0';
393 }
394 }
395 return (dn);
396 }
397
398 #endif
399
400