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