1 /* This is extracted from the OpenLDAP sources.
2  *
3  * Stuff that isn't used in e-book-backend-ldap.c was dropped, like
4  * the LDAPSchemaExtensionItem stuff.
5  *
6  * This file basically has three parts:
7  *
8  * - some general macros from OpenLDAP that work as such on all
9  *   implementations.
10  *
11  * - ldap_str2objectclass()
12  *
13  * - ldap_url_parse()
14  */
15 
16 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
17  *
18  * Copyright 1998-2005 The OpenLDAP Foundation.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted only as authorized by the OpenLDAP
23  * Public License.
24  *
25  * A copy of this license is available in file COPYING.OPENLDAP in
26  * the top-level directory of the distribution or, alternatively, at
27  * <http://www.OpenLDAP.org/license.html>.
28  */
29 
30 #include <string.h>
31 #include <assert.h>
32 
33 /* from various header files */
34 
35 #define LDAP_CONST const
36 
37 #define LDAP_PORT		389		/* ldap:///		default LDAP port */
38 #define LDAPS_PORT		636		/* ldaps:///	default LDAP over TLS port */
39 
40 #define LDAP_ROOT_DSE				""
41 
42 #define LDAP_SPACE(c)		((c) == ' ' || (c) == '\t' || (c) == '\n')
43 #define LDAP_DIGIT(c)		((c) >= '0' && (c) <= '9')
44 
45 #define LDAP_EXOP_START_TLS		"1.3.6.1.4.1.1466.20037"	/* RFC 2830 */
46 
47 #define LDAP_MALLOC(n) malloc((n))
48 #define LDAP_CALLOC(n,s) calloc((n),(s))
49 #define LDAP_REALLOC(p,s) realloc((p),(s))
50 #define LDAP_FREE(p) free((p))
51 #define LDAP_VFREE(p) vfree((gpointer *)(p))
52 #define LDAP_STRDUP(s) strdup((s))
53 
54 #define LDAP_RANGE(n,x,y)	(((x) <= (n)) && ((n) <= (y)))
55 #define LDAP_NAME_ERROR(n)	LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
56 
57 #define ldap_msgtype(lm) (lm)->lm_msgtype
58 #define ldap_msgid(lm) (lm)->lm_msgid
59 #ifndef LDAP_TYPE_OR_VALUE_EXISTS
60 #define LDAP_TYPE_OR_VALUE_EXISTS 0x14
61 #endif
62 #ifndef LDAP_SCOPE_DEFAULT
63 #define LDAP_SCOPE_DEFAULT -1
64 #endif
65 #ifndef LDAP_OPT_SUCCESS
66 #define LDAP_OPT_SUCCESS 0x00
67 #endif
68 #ifndef LDAP_INSUFFICIENT_ACCESS
69 #define LDAP_INSUFFICIENT_ACCESS 0x32
70 #endif
71 
72 #define LDAP_SCHERR_OUTOFMEM		1
73 #define LDAP_SCHERR_UNEXPTOKEN		2
74 #define LDAP_SCHERR_NOLEFTPAREN		3
75 #define LDAP_SCHERR_NORIGHTPAREN	4
76 #define LDAP_SCHERR_NODIGIT		5
77 #define LDAP_SCHERR_BADNAME		6
78 #define LDAP_SCHERR_BADDESC		7
79 #define LDAP_SCHERR_BADSUP		8
80 #define LDAP_SCHERR_DUPOPT		9
81 #define LDAP_SCHERR_EMPTY		10
82 #define LDAP_SCHERR_MISSING		11
83 #define LDAP_SCHERR_OUT_OF_ORDER	12
84 
85 #define LDAP_SCHEMA_YES				1
86 
87 #define LDAP_SCHEMA_ABSTRACT			0
88 #define LDAP_SCHEMA_STRUCTURAL			1
89 #define LDAP_SCHEMA_AUXILIARY			2
90 
91 #define LDAP_SCHEMA_ALLOW_NONE		0x00U /* Strict parsing               */
92 #define LDAP_SCHEMA_ALLOW_NO_OID	0x01U /* Allow missing oid            */
93 #define LDAP_SCHEMA_ALLOW_QUOTED	0x02U /* Allow bogus extra quotes     */
94 #define LDAP_SCHEMA_ALLOW_DESCR		0x04U /* Allow descr instead of OID   */
95 #define LDAP_SCHEMA_ALLOW_DESCR_PREFIX	0x08U /* Allow descr as OID prefix    */
96 #define LDAP_SCHEMA_ALLOW_OID_MACRO	0x10U /* Allow OID macros in slapd    */
97 #define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */
98 #define LDAP_SCHEMA_ALLOW_ALL		0x3fU /* Be very liberal in parsing   */
99 #define	LDAP_SCHEMA_SKIP			0x80U /* Don't malloc any result      */
100 
101 typedef struct ldap_objectclass {
102 	gchar *oc_oid;		/* REQUIRED */
103 	gchar **oc_names;	/* OPTIONAL */
104 	gchar *oc_desc;		/* OPTIONAL */
105 	gint  oc_obsolete;	/* 0=no, 1=yes */
106 	gchar **oc_sup_oids;	/* OPTIONAL */
107 	gint  oc_kind;		/* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */
108 	gchar **oc_at_oids_must;	/* OPTIONAL */
109 	gchar **oc_at_oids_may;	/* OPTIONAL */
110 } LDAPObjectClass;
111 
112 static void
vfree(gpointer * vec)113 vfree (gpointer *vec)
114 {
115   gint i;
116 
117   for (i = 0; vec[i] != NULL; i++)
118     free (vec[i]);
119 }
120 
121 /* from schema.c */
122 
123 /*
124  * Now come the parsers.  There is one parser for each entity type:
125  * objectclasses, attributetypes, etc.
126  *
127  * Each of them is written as a recursive-descent parser, except that
128  * none of them is really recursive.  But the idea is kept: there
129  * is one routine per non-terminal that eithers gobbles lexical tokens
130  * or calls lower-level routines, etc.
131  *
132  * The scanner is implemented in the routine get_token.  Actually,
133  * get_token is more than a scanner and will return tokens that are
134  * in fact non-terminals in the grammar.  So you can see the whole
135  * approach as the combination of a low-level bottom-up recognizer
136  * combined with a scanner and a number of top-down parsers.  Or just
137  * consider that the real grammars recognized by the parsers are not
138  * those of the standards.  As a matter of fact, our parsers are more
139  * liberal than the spec when there is no ambiguity.
140  *
141  * The difference is pretty academic (modulo bugs or incorrect
142  * interpretation of the specs).
143  */
144 
145 #define TK_NOENDQUOTE	-2
146 #define TK_OUTOFMEM	-1
147 #define TK_EOS		0
148 #define TK_UNEXPCHAR	1
149 #define TK_BAREWORD	2
150 #define TK_QDSTRING	3
151 #define TK_LEFTPAREN	4
152 #define TK_RIGHTPAREN	5
153 #define TK_DOLLAR	6
154 #define TK_QDESCR	TK_QDSTRING
155 
156 struct token {
157 	gint type;
158 	gchar *sval;
159 };
160 
161 static gint
get_token(const gchar ** sp,gchar ** token_val)162 get_token (const gchar **sp,
163            gchar **token_val)
164 {
165 	gint kind;
166 	const gchar *p;
167 	const gchar *q;
168 	gchar *res;
169 
170 	*token_val = NULL;
171 	switch (**sp) {
172 	case '\0':
173 		kind = TK_EOS;
174 		(*sp)++;
175 		break;
176 	case '(':
177 		kind = TK_LEFTPAREN;
178 		(*sp)++;
179 		break;
180 	case ')':
181 		kind = TK_RIGHTPAREN;
182 		(*sp)++;
183 		break;
184 	case '$':
185 		kind = TK_DOLLAR;
186 		(*sp)++;
187 		break;
188 	case '\'':
189 		kind = TK_QDSTRING;
190 		(*sp)++;
191 		p = *sp;
192 		while (**sp != '\'' && **sp != '\0')
193 			(*sp)++;
194 		if (**sp == '\'') {
195 			q = *sp;
196 			res = LDAP_MALLOC (q - p + 1);
197 			if (!res) {
198 				kind = TK_OUTOFMEM;
199 			} else {
200 				strncpy (res,p,q - p);
201 				res[q - p] = '\0';
202 				*token_val = res;
203 			}
204 			(*sp)++;
205 		} else {
206 			kind = TK_NOENDQUOTE;
207 		}
208 		break;
209 	default:
210 		kind = TK_BAREWORD;
211 		p = *sp;
212 		while (!LDAP_SPACE (**sp) &&
213 			**sp != '(' &&
214 			**sp != ')' &&
215 			**sp != '$' &&
216 			**sp != '\'' &&
217 			**sp != '\0')
218 			(*sp)++;
219 		q = *sp;
220 		res = LDAP_MALLOC (q - p + 1);
221 		if (!res) {
222 			kind = TK_OUTOFMEM;
223 		} else {
224 			strncpy (res,p,q - p);
225 			res[q - p] = '\0';
226 			*token_val = res;
227 		}
228 		break;
229 /*		kind = TK_UNEXPCHAR; */
230 /*		break; */
231 	}
232 
233 	return kind;
234 }
235 
236 /* Gobble optional whitespace */
237 static void
parse_whsp(const gchar ** sp)238 parse_whsp (const gchar **sp)
239 {
240 	while (LDAP_SPACE (**sp))
241 		(*sp)++;
242 }
243 
244 /* Parse a sequence of dot-separated decimal strings */
245 static gchar *
ldap_int_parse_numericoid(const gchar ** sp,gint * code,const gint flags)246 ldap_int_parse_numericoid (const gchar **sp, gint *code, const gint flags)
247 {
248 	gchar *res = NULL;
249 	const gchar *start = *sp;
250 	gint len;
251 	gint quoted = 0;
252 
253 	/* Netscape puts the SYNTAX value in quotes (incorrectly) */
254 	if (flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'') {
255 		quoted = 1;
256 		(*sp)++;
257 		start++;
258 	}
259 	/* Each iteration of this loop gets one decimal string */
260 	while (**sp) {
261 		if (!LDAP_DIGIT (**sp)) {
262 			/*
263 			 * Initial gchar is not a digit or gchar after dot is
264 			 * not a digit
265 			 */
266 			*code = LDAP_SCHERR_NODIGIT;
267 			return NULL;
268 		}
269 		(*sp)++;
270 		while (LDAP_DIGIT (**sp))
271 			(*sp)++;
272 		if (**sp != '.')
273 			break;
274 		/* Otherwise, gobble the dot and loop again */
275 		(*sp)++;
276 	}
277 	/* Now *sp points at the gchar past the numericoid. Perfect. */
278 	len = *sp - start;
279 	if (flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted) {
280 		if (**sp == '\'') {
281 			(*sp)++;
282 		} else {
283 			*code = LDAP_SCHERR_UNEXPTOKEN;
284 			return NULL;
285 		}
286 	}
287 	if (flags & LDAP_SCHEMA_SKIP) {
288 		res = (gchar *) start;
289 	} else {
290 		res = LDAP_MALLOC (len + 1);
291 		if (!res) {
292 			*code = LDAP_SCHERR_OUTOFMEM;
293 			return (NULL);
294 		}
295 		strncpy (res,start,len);
296 		res[len] = '\0';
297 	}
298 	return (res);
299 }
300 
301 /* Parse a qdescr or a list of them enclosed in () */
302 static gchar **
parse_qdescrs(const gchar ** sp,gint * code)303 parse_qdescrs (const gchar **sp, gint *code)
304 {
305 	gchar ** res;
306 	gchar ** res1;
307 	gint kind;
308 	gchar *sval;
309 	gint size;
310 	gint pos;
311 
312 	parse_whsp (sp);
313 	kind = get_token (sp,&sval);
314 	if (kind == TK_LEFTPAREN) {
315 		/* Let's presume there will be at least 2 entries */
316 		size = 3;
317 		res = LDAP_CALLOC (3,sizeof (gchar *));
318 		if (!res) {
319 			*code = LDAP_SCHERR_OUTOFMEM;
320 			return NULL;
321 		}
322 		pos = 0;
323 		while (1) {
324 			parse_whsp (sp);
325 			kind = get_token (sp,&sval);
326 			if (kind == TK_RIGHTPAREN)
327 				break;
328 			if (kind == TK_QDESCR) {
329 				if (pos == size - 2) {
330 					size++;
331 					res1 = LDAP_REALLOC (res,size * sizeof (gchar *));
332 					if (!res1) {
333 						LDAP_VFREE (res);
334 						LDAP_FREE (sval);
335 						*code = LDAP_SCHERR_OUTOFMEM;
336 						return (NULL);
337 					}
338 					res = res1;
339 				}
340 				res[pos++] = sval;
341 				res[pos] = NULL;
342 				parse_whsp (sp);
343 			} else {
344 				LDAP_VFREE (res);
345 				LDAP_FREE (sval);
346 				*code = LDAP_SCHERR_UNEXPTOKEN;
347 				return (NULL);
348 			}
349 		}
350 		parse_whsp (sp);
351 		return (res);
352 	} else if (kind == TK_QDESCR) {
353 		res = LDAP_CALLOC (2,sizeof (gchar *));
354 		if (!res) {
355 			*code = LDAP_SCHERR_OUTOFMEM;
356 			return NULL;
357 		}
358 		res[0] = sval;
359 		res[1] = NULL;
360 		parse_whsp (sp);
361 		return res;
362 	} else {
363 		LDAP_FREE (sval);
364 		*code = LDAP_SCHERR_BADNAME;
365 		return NULL;
366 	}
367 }
368 
369 /* Parse a woid or a $-separated list of them enclosed in () */
370 static gchar **
parse_oids(const gchar ** sp,gint * code,const gint allow_quoted)371 parse_oids (const gchar **sp, gint *code, const gint allow_quoted)
372 {
373 	gchar ** res;
374 	gchar ** res1;
375 	gint kind;
376 	gchar *sval;
377 	gint size;
378 	gint pos;
379 
380 	/*
381 	 * Strictly speaking, doing this here accepts whsp before the
382 	 * ( at the begining of an oidlist, but this is harmless.  Also,
383 	 * we are very liberal in what we accept as an OID.  Maybe
384 	 * refine later.
385 	 */
386 	parse_whsp (sp);
387 	kind = get_token (sp,&sval);
388 	if (kind == TK_LEFTPAREN) {
389 		/* Let's presume there will be at least 2 entries */
390 		size = 3;
391 		res = LDAP_CALLOC (3,sizeof (gchar *));
392 		if (!res) {
393 			*code = LDAP_SCHERR_OUTOFMEM;
394 			return NULL;
395 		}
396 		pos = 0;
397 		parse_whsp (sp);
398 		kind = get_token (sp,&sval);
399 		if (kind == TK_BAREWORD ||
400 		     (allow_quoted && kind == TK_QDSTRING)) {
401 			res[pos++] = sval;
402 			res[pos] = NULL;
403 		} else {
404 			*code = LDAP_SCHERR_UNEXPTOKEN;
405 			LDAP_FREE (sval);
406 			LDAP_VFREE (res);
407 			/* cppcheck-suppress memleak */
408 			return NULL;
409 		}
410 		parse_whsp (sp);
411 		while (1) {
412 			kind = get_token (sp,&sval);
413 			if (kind == TK_RIGHTPAREN)
414 				break;
415 			if (kind == TK_DOLLAR) {
416 				parse_whsp (sp);
417 				kind = get_token (sp,&sval);
418 				if (kind == TK_BAREWORD ||
419 				     (allow_quoted &&
420 				       kind == TK_QDSTRING)) {
421 					if (pos == size - 2) {
422 						size++;
423 						res1 = LDAP_REALLOC (res,size * sizeof (gchar *));
424 						if (!res1) {
425 							LDAP_FREE (sval);
426 							LDAP_VFREE (res);
427 							*code = LDAP_SCHERR_OUTOFMEM;
428 							return (NULL);
429 						}
430 						res = res1;
431 					}
432 					res[pos++] = sval;
433 					res[pos] = NULL;
434 				} else {
435 					*code = LDAP_SCHERR_UNEXPTOKEN;
436 					LDAP_FREE (sval);
437 					LDAP_VFREE (res);
438 					return NULL;
439 				}
440 				parse_whsp (sp);
441 			} else {
442 				*code = LDAP_SCHERR_UNEXPTOKEN;
443 				LDAP_FREE (sval);
444 				LDAP_VFREE (res);
445 				return NULL;
446 			}
447 		}
448 		parse_whsp (sp);
449 		return (res);
450 	} else if (kind == TK_BAREWORD ||
451 		    (allow_quoted && kind == TK_QDSTRING)) {
452 		res = LDAP_CALLOC (2,sizeof (gchar *));
453 		if (!res) {
454 			LDAP_FREE (sval);
455 			*code = LDAP_SCHERR_OUTOFMEM;
456 			return NULL;
457 		}
458 		res[0] = sval;
459 		res[1] = NULL;
460 		parse_whsp (sp);
461 		return res;
462 	} else {
463 		LDAP_FREE (sval);
464 		*code = LDAP_SCHERR_BADNAME;
465 		return NULL;
466 	}
467 }
468 
469 static void
ldap_objectclass_free(LDAPObjectClass * oc)470 ldap_objectclass_free (LDAPObjectClass *oc)
471 {
472 	LDAP_FREE (oc->oc_oid);
473 	if (oc->oc_names) LDAP_VFREE (oc->oc_names);
474 	if (oc->oc_desc) LDAP_FREE (oc->oc_desc);
475 	if (oc->oc_sup_oids) LDAP_VFREE (oc->oc_sup_oids);
476 	if (oc->oc_at_oids_must) LDAP_VFREE (oc->oc_at_oids_must);
477 	if (oc->oc_at_oids_may) LDAP_VFREE (oc->oc_at_oids_may);
478 	LDAP_FREE (oc);
479 }
480 
481 static LDAPObjectClass *
ldap_str2objectclass(LDAP_CONST gchar * s,gint * code,LDAP_CONST gchar ** errp,LDAP_CONST unsigned flags)482 ldap_str2objectclass (LDAP_CONST gchar *s,
483                       gint *code,
484                       LDAP_CONST gchar **errp,
485                       LDAP_CONST unsigned flags)
486 {
487 	gint kind;
488 	const gchar *ss = s;
489 	gchar *sval;
490 	gint seen_name = 0;
491 	gint seen_desc = 0;
492 	gint seen_obsolete = 0;
493 	gint seen_sup = 0;
494 	gint seen_kind = 0;
495 	gint seen_must = 0;
496 	gint seen_may = 0;
497 	LDAPObjectClass *oc;
498 	gchar ** ext_vals;
499 	const gchar *savepos;
500 
501 	if (!s) {
502 		*code = LDAP_SCHERR_EMPTY;
503 		*errp = "";
504 		return NULL;
505 	}
506 
507 	*errp = s;
508 	oc = LDAP_CALLOC (1,sizeof (LDAPObjectClass));
509 
510 	if (!oc) {
511 		*code = LDAP_SCHERR_OUTOFMEM;
512 		return NULL;
513 	}
514 	oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
515 
516 	kind = get_token (&ss,&sval);
517 	if (kind != TK_LEFTPAREN) {
518 		*code = LDAP_SCHERR_NOLEFTPAREN;
519 		LDAP_FREE (sval);
520 		ldap_objectclass_free (oc);
521 		return NULL;
522 	}
523 
524 	/*
525 	 * Definitions MUST begin with an OID in the numericoid format.
526 	 * However, this routine is used by clients to parse the response
527 	 * from servers and very well known servers will provide an OID
528 	 * in the wrong format or even no OID at all.  We do our best to
529 	 * extract info from those servers.
530 	 */
531 	parse_whsp (&ss);
532 	savepos = ss;
533 	oc->oc_oid = ldap_int_parse_numericoid (&ss,code,0);
534 	if (!oc->oc_oid) {
535 		if ((flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos)) {
536 			/* Backtracking */
537 			ss = savepos;
538 			kind = get_token (&ss,&sval);
539 			if (kind == TK_BAREWORD) {
540 				if (!strcasecmp (sval, "NAME") ||
541 				    !strcasecmp (sval, "DESC") ||
542 				    !strcasecmp (sval, "OBSOLETE") ||
543 				    !strcasecmp (sval, "SUP") ||
544 				    !strcasecmp (sval, "ABSTRACT") ||
545 				    !strcasecmp (sval, "STRUCTURAL") ||
546 				    !strcasecmp (sval, "AUXILIARY") ||
547 				    !strcasecmp (sval, "MUST") ||
548 				    !strcasecmp (sval, "MAY") ||
549 				    !strncasecmp (sval, "X-", 2)) {
550 					/* Missing OID, backtrack */
551 					ss = savepos;
552 				} else if (flags &
553 					LDAP_SCHEMA_ALLOW_OID_MACRO) {
554 					/* Non-numerical OID, ignore */
555 					gint len = ss - savepos;
556 					oc->oc_oid = LDAP_MALLOC (len + 1);
557 					strncpy (oc->oc_oid, savepos, len);
558 					oc->oc_oid[len] = 0;
559 				}
560 			}
561 			LDAP_FREE (sval);
562 		} else {
563 			*errp = ss;
564 			ldap_objectclass_free (oc);
565 			return NULL;
566 		}
567 	}
568 	parse_whsp (&ss);
569 
570 	/*
571 	 * Beyond this point we will be liberal an accept the items
572 	 * in any order.
573 	 */
574 	while (1) {
575 		kind = get_token (&ss,&sval);
576 		switch (kind) {
577 		case TK_EOS:
578 			*code = LDAP_SCHERR_NORIGHTPAREN;
579 			*errp = ss;
580 			ldap_objectclass_free (oc);
581 			return NULL;
582 		case TK_RIGHTPAREN:
583 			return oc;
584 		case TK_BAREWORD:
585 			if (!strcasecmp (sval,"NAME")) {
586 				LDAP_FREE (sval);
587 				if (seen_name) {
588 					*code = LDAP_SCHERR_DUPOPT;
589 					*errp = ss;
590 					ldap_objectclass_free (oc);
591 					return (NULL);
592 				}
593 				seen_name = 1;
594 				oc->oc_names = parse_qdescrs (&ss,code);
595 				if (!oc->oc_names) {
596 					if (*code != LDAP_SCHERR_OUTOFMEM)
597 						*code = LDAP_SCHERR_BADNAME;
598 					*errp = ss;
599 					ldap_objectclass_free (oc);
600 					return NULL;
601 				}
602 			} else if (!strcasecmp (sval,"DESC")) {
603 				LDAP_FREE (sval);
604 				if (seen_desc) {
605 					*code = LDAP_SCHERR_DUPOPT;
606 					*errp = ss;
607 					ldap_objectclass_free (oc);
608 					return (NULL);
609 				}
610 				seen_desc = 1;
611 				parse_whsp (&ss);
612 				kind = get_token (&ss,&sval);
613 				if (kind != TK_QDSTRING) {
614 					*code = LDAP_SCHERR_UNEXPTOKEN;
615 					*errp = ss;
616 					LDAP_FREE (sval);
617 					ldap_objectclass_free (oc);
618 					return NULL;
619 				}
620 				oc->oc_desc = sval;
621 				parse_whsp (&ss);
622 			} else if (!strcasecmp (sval,"OBSOLETE")) {
623 				LDAP_FREE (sval);
624 				if (seen_obsolete) {
625 					*code = LDAP_SCHERR_DUPOPT;
626 					*errp = ss;
627 					ldap_objectclass_free (oc);
628 					return (NULL);
629 				}
630 				seen_obsolete = 1;
631 				oc->oc_obsolete = LDAP_SCHEMA_YES;
632 				parse_whsp (&ss);
633 			} else if (!strcasecmp (sval,"SUP")) {
634 				LDAP_FREE (sval);
635 				if (seen_sup) {
636 					*code = LDAP_SCHERR_DUPOPT;
637 					*errp = ss;
638 					ldap_objectclass_free (oc);
639 					return (NULL);
640 				}
641 				seen_sup = 1;
642 				oc->oc_sup_oids = parse_oids (&ss,
643 							     code,
644 							     flags);
645 				if (!oc->oc_sup_oids) {
646 					*errp = ss;
647 					ldap_objectclass_free (oc);
648 					return NULL;
649 				}
650 			} else if (!strcasecmp (sval,"ABSTRACT")) {
651 				LDAP_FREE (sval);
652 				if (seen_kind) {
653 					*code = LDAP_SCHERR_DUPOPT;
654 					*errp = ss;
655 					ldap_objectclass_free (oc);
656 					return (NULL);
657 				}
658 				seen_kind = 1;
659 				oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
660 				parse_whsp (&ss);
661 			} else if (!strcasecmp (sval,"STRUCTURAL")) {
662 				LDAP_FREE (sval);
663 				if (seen_kind) {
664 					*code = LDAP_SCHERR_DUPOPT;
665 					*errp = ss;
666 					ldap_objectclass_free (oc);
667 					return (NULL);
668 				}
669 				seen_kind = 1;
670 				oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
671 				parse_whsp (&ss);
672 			} else if (!strcasecmp (sval,"AUXILIARY")) {
673 				LDAP_FREE (sval);
674 				if (seen_kind) {
675 					*code = LDAP_SCHERR_DUPOPT;
676 					*errp = ss;
677 					ldap_objectclass_free (oc);
678 					return (NULL);
679 				}
680 				seen_kind = 1;
681 				oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
682 				parse_whsp (&ss);
683 			} else if (!strcasecmp (sval,"MUST")) {
684 				LDAP_FREE (sval);
685 				if (seen_must) {
686 					*code = LDAP_SCHERR_DUPOPT;
687 					*errp = ss;
688 					ldap_objectclass_free (oc);
689 					return (NULL);
690 				}
691 				seen_must = 1;
692 				oc->oc_at_oids_must = parse_oids (&ss,code,0);
693 				if (!oc->oc_at_oids_must) {
694 					*errp = ss;
695 					ldap_objectclass_free (oc);
696 					return NULL;
697 				}
698 				parse_whsp (&ss);
699 			} else if (!strcasecmp (sval,"MAY")) {
700 				LDAP_FREE (sval);
701 				if (seen_may) {
702 					*code = LDAP_SCHERR_DUPOPT;
703 					*errp = ss;
704 					ldap_objectclass_free (oc);
705 					return (NULL);
706 				}
707 				seen_may = 1;
708 				oc->oc_at_oids_may = parse_oids (&ss,code,0);
709 				if (!oc->oc_at_oids_may) {
710 					*errp = ss;
711 					ldap_objectclass_free (oc);
712 					return NULL;
713 				}
714 				parse_whsp (&ss);
715 			} else if (sval[0] == 'X' && sval[1] == '-') {
716 				/* Should be parse_qdstrings */
717 				ext_vals = parse_qdescrs (&ss, code);
718 				if (!ext_vals) {
719 					*errp = ss;
720 					ldap_objectclass_free (oc);
721 					return NULL;
722 				}
723 #if 0
724 				if (add_extension (&oc->oc_extensions,
725 						    sval, ext_vals)) {
726 					*code = LDAP_SCHERR_OUTOFMEM;
727 					*errp = ss;
728 					LDAP_FREE (sval);
729 					ldap_objectclass_free (oc);
730 					return NULL;
731 				}
732 #endif
733 			} else {
734 				*code = LDAP_SCHERR_UNEXPTOKEN;
735 				*errp = ss;
736 				LDAP_FREE (sval);
737 				ldap_objectclass_free (oc);
738 				return NULL;
739 			}
740 			break;
741 		default:
742 			*code = LDAP_SCHERR_UNEXPTOKEN;
743 			*errp = ss;
744 			LDAP_FREE (sval);
745 			ldap_objectclass_free (oc);
746 			return NULL;
747 		}
748 	}
749 }
750 
751 /* from utf-8.c */
752 
753 #define LDAP_UTF8_NEXT(p) g_utf8_next_char((p))
754 #define LDAP_UTF8_INCR(p) ((p)=LDAP_UTF8_NEXT((p)))
755 #define ldap_x_utf8_to_ucs4(str) g_utf8_get_char(str)
756 
757 static gchar *
ldap_utf8_strchr(const gchar * str,const gchar * chr)758 ldap_utf8_strchr (const gchar *str,
759                   const gchar *chr)
760 {
761 	for (; *str != '\0'; LDAP_UTF8_INCR (str)) {
762 		if (ldap_x_utf8_to_ucs4 (str) == ldap_x_utf8_to_ucs4 (chr)) {
763 			return (gchar *) str;
764 		}
765 	}
766 
767 	return NULL;
768 }
769 
770 static gsize
ldap_utf8_strcspn(const gchar * str,const gchar * set)771 ldap_utf8_strcspn (const gchar *str,
772                    const gchar *set)
773 {
774 	const gchar *cstr;
775 	const gchar *cset;
776 
777 	for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) {
778 		for (cset = set; *cset != '\0'; LDAP_UTF8_INCR (cset)) {
779 			if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) {
780 				return cstr - str;
781 			}
782 		}
783 	}
784 
785 	return cstr - str;
786 }
787 
788 static gsize
ldap_utf8_strspn(const gchar * str,const gchar * set)789 ldap_utf8_strspn (const gchar *str,
790                   const gchar *set)
791 {
792 	const gchar *cstr;
793 	const gchar *cset;
794 
795 	for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) {
796 		for (cset = set; ; LDAP_UTF8_INCR (cset)) {
797 			if (*cset == '\0') {
798 				return cstr - str;
799 			}
800 
801 			if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) {
802 				break;
803 			}
804 		}
805 	}
806 
807 	return cstr - str;
808 }
809 
ldap_utf8_strtok(gchar * str,const gchar * sep,gchar ** last)810 static gchar *ldap_utf8_strtok (gchar *str, const gchar *sep, gchar **last)
811 {
812 	gchar *begin;
813 	gchar *end;
814 
815 	if (last == NULL) return NULL;
816 
817 	begin = str ? str : *last;
818 
819 	begin += ldap_utf8_strspn (begin, sep);
820 
821 	if (*begin == '\0') {
822 		*last = NULL;
823 		return NULL;
824 	}
825 
826 	end = &begin[ ldap_utf8_strcspn (begin, sep) ];
827 
828 	if (*end != '\0') {
829 		gchar *next = LDAP_UTF8_NEXT (end);
830 		*end = '\0';
831 		end = next;
832 	}
833 
834 	*last = end;
835 	return begin;
836 }
837 
838 /* from ldap.h */
839 
840 #define LDAP_URL_SUCCESS		0x00	/* Success */
841 #define LDAP_URL_ERR_MEM		0x01	/* can't allocate memory space */
842 #define LDAP_URL_ERR_PARAM		0x02	/* parameter is bad */
843 
844 #define LDAP_URL_ERR_BADSCHEME	0x03	/* URL doesn't begin with "ldap[si]://" */
845 #define LDAP_URL_ERR_BADENCLOSURE 0x04	/* URL is missing trailing ">" */
846 #define LDAP_URL_ERR_BADURL		0x05	/* URL is bad */
847 #define LDAP_URL_ERR_BADHOST	0x06	/* host port is bad */
848 #define LDAP_URL_ERR_BADATTRS	0x07	/* bad (or missing) attributes */
849 #define LDAP_URL_ERR_BADSCOPE	0x08	/* scope string is invalid (or missing) */
850 #define LDAP_URL_ERR_BADFILTER	0x09	/* bad or missing filter */
851 #define LDAP_URL_ERR_BADEXTS	0x0a	/* bad or missing extensions */
852 
853 #define LDAP_URL_PREFIX         "ldap://"
854 #define LDAP_URL_PREFIX_LEN     (sizeof(LDAP_URL_PREFIX)-1)
855 #define LDAPS_URL_PREFIX		"ldaps://"
856 #define LDAPS_URL_PREFIX_LEN	(sizeof(LDAPS_URL_PREFIX)-1)
857 #define LDAPI_URL_PREFIX	"ldapi://"
858 #define LDAPI_URL_PREFIX_LEN	(sizeof(LDAPI_URL_PREFIX)-1)
859 
860 #define LDAP_URL_URLCOLON		"URL:"
861 #define LDAP_URL_URLCOLON_LEN	(sizeof(LDAP_URL_URLCOLON)-1)
862 
863 typedef struct ldap_url_desc {
864 	struct ldap_url_desc *lud_next;
865 	gchar	*lud_scheme;
866 	gchar	*lud_host;
867 	gint		lud_port;
868 	gchar	*lud_dn;
869 	gchar	**lud_attrs;
870 	gint		lud_scope;
871 	gchar	*lud_filter;
872 	gchar	**lud_exts;
873 	gint		lud_crit_exts;
874 } LDAPURLDesc;
875 
876 /* from url.c */
877 
878 static const gchar *
skip_url_prefix(const gchar * url,gint * enclosedp,const gchar ** scheme)879 skip_url_prefix (
880 	const gchar *url,
881 	gint *enclosedp,
882 	const gchar **scheme)
883 {
884 	/*
885 	 * return non-zero if this looks like a LDAP URL; zero if not
886 	 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
887 	 */
888 	const gchar *p;
889 
890 	if (url == NULL) {
891 		return (NULL);
892 	}
893 
894 	p = url;
895 
896 	/* skip leading '<' (if any) */
897 	if (*p == '<') {
898 		*enclosedp = 1;
899 		++p;
900 	} else {
901 		*enclosedp = 0;
902 	}
903 
904 	/* skip leading "URL:" (if any) */
905 	if (strncasecmp (p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN) == 0) {
906 		p += LDAP_URL_URLCOLON_LEN;
907 	}
908 
909 	/* check for "ldap://" prefix */
910 	if (strncasecmp (p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN) == 0) {
911 		/* skip over "ldap://" prefix and return success */
912 		p += LDAP_URL_PREFIX_LEN;
913 		*scheme = "ldap";
914 		return (p);
915 	}
916 
917 	/* check for "ldaps://" prefix */
918 	if (strncasecmp (p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN) == 0) {
919 		/* skip over "ldaps://" prefix and return success */
920 		p += LDAPS_URL_PREFIX_LEN;
921 		*scheme = "ldaps";
922 		return (p);
923 	}
924 
925 	/* check for "ldapi://" prefix */
926 	if (strncasecmp (p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN) == 0) {
927 		/* skip over "ldapi://" prefix and return success */
928 		p += LDAPI_URL_PREFIX_LEN;
929 		*scheme = "ldapi";
930 		return (p);
931 	}
932 
933 #ifdef LDAP_CONNECTIONLESS
934 	/* check for "cldap://" prefix */
935 	if (strncasecmp (p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN) == 0) {
936 		/* skip over "cldap://" prefix and return success */
937 		p += LDAPC_URL_PREFIX_LEN;
938 		*scheme = "cldap";
939 		return (p);
940 	}
941 #endif
942 
943 	return (NULL);
944 }
945 
946 static gint
str2scope(const gchar * p)947 str2scope (const gchar *p)
948 {
949 	if (strcasecmp (p, "one") == 0) {
950 		return LDAP_SCOPE_ONELEVEL;
951 
952 	} else if (strcasecmp (p, "onelevel") == 0) {
953 		return LDAP_SCOPE_ONELEVEL;
954 
955 	} else if (strcasecmp (p, "base") == 0) {
956 		return LDAP_SCOPE_BASE;
957 
958 	} else if (strcasecmp (p, "sub") == 0) {
959 		return LDAP_SCOPE_SUBTREE;
960 
961 	} else if (strcasecmp (p, "subtree") == 0) {
962 		return LDAP_SCOPE_SUBTREE;
963 	}
964 
965 	return (-1);
966 }
967 
968 static void
ldap_free_urldesc(LDAPURLDesc * ludp)969 ldap_free_urldesc (LDAPURLDesc *ludp)
970 {
971 	if (ludp == NULL) {
972 		return;
973 	}
974 
975 	if (ludp->lud_scheme != NULL) {
976 		LDAP_FREE (ludp->lud_scheme);
977 	}
978 
979 	if (ludp->lud_host != NULL) {
980 		LDAP_FREE (ludp->lud_host);
981 	}
982 
983 	if (ludp->lud_dn != NULL) {
984 		LDAP_FREE (ludp->lud_dn);
985 	}
986 
987 	if (ludp->lud_filter != NULL) {
988 		LDAP_FREE (ludp->lud_filter);
989 	}
990 
991 	if (ludp->lud_attrs != NULL) {
992 		LDAP_VFREE (ludp->lud_attrs);
993 	}
994 
995 	if (ludp->lud_exts != NULL) {
996 		LDAP_VFREE (ludp->lud_exts);
997 	}
998 
999 	LDAP_FREE (ludp);
1000 }
1001 
1002 static gint
ldap_int_unhex(gint c)1003 ldap_int_unhex (gint c)
1004 {
1005 	return (c >= '0' && c <= '9' ? c - '0'
1006 	    : c >= 'A' && c <= 'F' ? c - 'A' + 10
1007 	    : c - 'a' + 10);
1008 }
1009 
1010 static void
ldap_pvt_hex_unescape(gchar * s)1011 ldap_pvt_hex_unescape (gchar *s)
1012 {
1013 	/*
1014 	 * Remove URL hex escapes from s... done in place.  The basic concept for
1015 	 * this routine is borrowed from the WWW library HTUnEscape() routine.
1016 	 */
1017 	gchar	*p;
1018 
1019 	for (p = s; *s != '\0'; ++s) {
1020 		if (*s == '%') {
1021 			if (*++s == '\0') {
1022 				break;
1023 			}
1024 			*p = ldap_int_unhex(*s) << 4;
1025 			if (*++s == '\0') {
1026 				break;
1027 			}
1028 			*p++ += ldap_int_unhex(*s);
1029 		} else {
1030 			*p++ = *s;
1031 		}
1032 	}
1033 
1034 	*p = '\0';
1035 }
1036 
1037 static gchar **
ldap_str2charray(const gchar * str_in,const gchar * brkstr)1038 ldap_str2charray (const gchar *str_in,
1039                   const gchar *brkstr)
1040 {
1041 	gchar	**res;
1042 	gchar	*str, *s;
1043 	gchar	*lasts;
1044 	gint	i;
1045 
1046 	/* protect the input string from strtok */
1047 	str = LDAP_STRDUP (str_in);
1048 	if (str == NULL) {
1049 		return NULL;
1050 	}
1051 
1052 	i = 1;
1053 	for (s = str; *s; s++) {
1054 		if (ldap_utf8_strchr (brkstr, s) != NULL) {
1055 			i++;
1056 		}
1057 	}
1058 
1059 	res = (gchar **) LDAP_MALLOC ((i + 1) * sizeof (gchar *));
1060 
1061 	if (res == NULL) {
1062 		LDAP_FREE (str);
1063 		return NULL;
1064 	}
1065 
1066 	i = 0;
1067 
1068 	for (s = ldap_utf8_strtok (str, brkstr, &lasts);
1069 		s != NULL;
1070 		s = ldap_utf8_strtok (NULL, brkstr, &lasts))
1071 	{
1072 		res[i] = LDAP_STRDUP (s);
1073 
1074 		if (res[i] == NULL) {
1075 			for (--i; i >= 0; i--) {
1076 				LDAP_FREE (res[i]);
1077 			}
1078 			LDAP_FREE (res);
1079 			LDAP_FREE (str);
1080 			return NULL;
1081 		}
1082 
1083 		i++;
1084 	}
1085 
1086 	res[i] = NULL;
1087 
1088 	LDAP_FREE (str);
1089 	return (res);
1090 }
1091 
1092 static gint
ldap_url_parse_ext(LDAP_CONST gchar * url_in,LDAPURLDesc ** ludpp)1093 ldap_url_parse_ext (LDAP_CONST gchar *url_in,
1094                     LDAPURLDesc **ludpp)
1095 {
1096 /*
1097  *  Pick apart the pieces of an LDAP URL.
1098  */
1099 
1100 	LDAPURLDesc	*ludp;
1101 	gchar	*p, *q, *r;
1102 	gint		i, enclosed;
1103 	const gchar *scheme = NULL;
1104 	const gchar *url_tmp;
1105 	gchar *url;
1106 
1107 	if (url_in == NULL || ludpp == NULL) {
1108 		return LDAP_URL_ERR_PARAM;
1109 	}
1110 
1111 	*ludpp = NULL;	/* pessimistic */
1112 
1113 	url_tmp = skip_url_prefix (url_in, &enclosed, &scheme);
1114 
1115 	if (url_tmp == NULL) {
1116 		return LDAP_URL_ERR_BADSCHEME;
1117 	}
1118 
1119 	assert (scheme);
1120 
1121 	/* make working copy of the remainder of the URL */
1122 	url = LDAP_STRDUP (url_tmp);
1123 	if (url == NULL) {
1124 		return LDAP_URL_ERR_MEM;
1125 	}
1126 
1127 	if (enclosed) {
1128 		p = &url[strlen (url) - 1];
1129 
1130 		if (*p != '>') {
1131 			LDAP_FREE (url);
1132 			return LDAP_URL_ERR_BADENCLOSURE;
1133 		}
1134 
1135 		*p = '\0';
1136 	}
1137 
1138 	/* allocate return struct */
1139 	ludp = (LDAPURLDesc *) LDAP_CALLOC (1, sizeof (LDAPURLDesc));
1140 
1141 	if (ludp == NULL) {
1142 		LDAP_FREE (url);
1143 		return LDAP_URL_ERR_MEM;
1144 	}
1145 
1146 	ludp->lud_next = NULL;
1147 	ludp->lud_host = NULL;
1148 	ludp->lud_port = 0;
1149 	ludp->lud_dn = NULL;
1150 	ludp->lud_attrs = NULL;
1151 	ludp->lud_filter = NULL;
1152 	ludp->lud_scope = LDAP_SCOPE_DEFAULT;
1153 	ludp->lud_filter = NULL;
1154 	ludp->lud_exts = NULL;
1155 
1156 	ludp->lud_scheme = LDAP_STRDUP (scheme);
1157 
1158 	if (ludp->lud_scheme == NULL) {
1159 		LDAP_FREE (url);
1160 		ldap_free_urldesc (ludp);
1161 		return LDAP_URL_ERR_MEM;
1162 	}
1163 
1164 	/* scan forward for '/' that marks end of hostport and begin. of dn */
1165 	p = strchr (url, '/');
1166 
1167 	if (p != NULL) {
1168 		/* terminate hostport; point to start of dn */
1169 		*p++ = '\0';
1170 	}
1171 
1172 	/* IPv6 syntax with [ip address]:port */
1173 	if (*url == '[') {
1174 		r = strchr (url, ']');
1175 		if (r == NULL) {
1176 			LDAP_FREE (url);
1177 			ldap_free_urldesc (ludp);
1178 			return LDAP_URL_ERR_BADURL;
1179 		}
1180 		*r++ = '\0';
1181 		q = strchr (r, ':');
1182 	} else {
1183 		q = strchr (url, ':');
1184 	}
1185 
1186 	if (q != NULL) {
1187 		gchar	*next;
1188 
1189 		*q++ = '\0';
1190 		ldap_pvt_hex_unescape (q);
1191 
1192 		if (*q == '\0') {
1193 			LDAP_FREE (url);
1194 			ldap_free_urldesc (ludp);
1195 			return LDAP_URL_ERR_BADURL;
1196 		}
1197 
1198 		ludp->lud_port = strtol (q, &next, 10);
1199 		if (next == NULL || next[0] != '\0') {
1200 			LDAP_FREE (url);
1201 			ldap_free_urldesc (ludp);
1202 			return LDAP_URL_ERR_BADURL;
1203 		}
1204 	}
1205 
1206 	ldap_pvt_hex_unescape (url);
1207 
1208 	/* If [ip address]:port syntax, url is [ip and we skip the [ */
1209 	ludp->lud_host = LDAP_STRDUP (url + (*url == '['));
1210 
1211 	if (ludp->lud_host == NULL) {
1212 		LDAP_FREE (url);
1213 		ldap_free_urldesc (ludp);
1214 		return LDAP_URL_ERR_MEM;
1215 	}
1216 
1217 	/*
1218 	 * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
1219 	 *
1220 	 * On early Novell releases, search references/referrals were returned
1221 	 * in this format, i.e., the dn was kind of in the scope position,
1222 	 * but the required slash is missing. The whole thing is illegal syntax,
1223 	 * but we need to account for it. Fortunately it can't be confused with
1224 	 * anything real.
1225 	 */
1226 	if ((p == NULL) && (q != NULL) && ((q = strchr (q, '?')) != NULL)) {
1227 		q++;
1228 		/* ? immediately followed by question */
1229 		if (*q == '?') {
1230 			q++;
1231 			if (*q != '\0') {
1232 				/* parse dn part */
1233 				ldap_pvt_hex_unescape (q);
1234 				ludp->lud_dn = LDAP_STRDUP (q);
1235 			} else {
1236 				ludp->lud_dn = LDAP_STRDUP ("");
1237 			}
1238 
1239 			if (ludp->lud_dn == NULL) {
1240 				LDAP_FREE (url);
1241 				ldap_free_urldesc (ludp);
1242 				return LDAP_URL_ERR_MEM;
1243 			}
1244 		}
1245 	}
1246 
1247 	if (p == NULL) {
1248 		LDAP_FREE (url);
1249 		*ludpp = ludp;
1250 		return LDAP_URL_SUCCESS;
1251 	}
1252 
1253 	/* scan forward for '?' that may marks end of dn */
1254 	q = strchr (p, '?');
1255 
1256 	if (q != NULL) {
1257 		/* terminate dn part */
1258 		*q++ = '\0';
1259 	}
1260 
1261 	if (*p != '\0') {
1262 		/* parse dn part */
1263 		ldap_pvt_hex_unescape (p);
1264 		ludp->lud_dn = LDAP_STRDUP (p);
1265 	} else {
1266 		ludp->lud_dn = LDAP_STRDUP ("");
1267 	}
1268 
1269 	if (ludp->lud_dn == NULL) {
1270 		LDAP_FREE (url);
1271 		ldap_free_urldesc (ludp);
1272 		return LDAP_URL_ERR_MEM;
1273 	}
1274 
1275 	if (q == NULL) {
1276 		/* no more */
1277 		LDAP_FREE (url);
1278 		*ludpp = ludp;
1279 		return LDAP_URL_SUCCESS;
1280 	}
1281 
1282 	/* scan forward for '?' that may marks end of attributes */
1283 	p = q;
1284 	q = strchr (p, '?');
1285 
1286 	if (q != NULL) {
1287 		/* terminate attributes part */
1288 		*q++ = '\0';
1289 	}
1290 
1291 	if (*p != '\0') {
1292 		/* parse attributes */
1293 		ldap_pvt_hex_unescape (p);
1294 		ludp->lud_attrs = ldap_str2charray (p, ",");
1295 
1296 		if (ludp->lud_attrs == NULL) {
1297 			LDAP_FREE (url);
1298 			ldap_free_urldesc (ludp);
1299 			return LDAP_URL_ERR_BADATTRS;
1300 		}
1301 	}
1302 
1303 	if (q == NULL) {
1304 		/* no more */
1305 		LDAP_FREE (url);
1306 		*ludpp = ludp;
1307 		return LDAP_URL_SUCCESS;
1308 	}
1309 
1310 	/* scan forward for '?' that may marks end of scope */
1311 	p = q;
1312 	q = strchr (p, '?');
1313 
1314 	if (q != NULL) {
1315 		/* terminate the scope part */
1316 		*q++ = '\0';
1317 	}
1318 
1319 	if (*p != '\0') {
1320 		/* parse the scope */
1321 		ldap_pvt_hex_unescape (p);
1322 		ludp->lud_scope = str2scope (p);
1323 
1324 		if (ludp->lud_scope == -1) {
1325 			LDAP_FREE (url);
1326 			ldap_free_urldesc (ludp);
1327 			return LDAP_URL_ERR_BADSCOPE;
1328 		}
1329 	}
1330 
1331 	if (q == NULL) {
1332 		/* no more */
1333 		LDAP_FREE (url);
1334 		*ludpp = ludp;
1335 		return LDAP_URL_SUCCESS;
1336 	}
1337 
1338 	/* scan forward for '?' that may marks end of filter */
1339 	p = q;
1340 	q = strchr (p, '?');
1341 
1342 	if (q != NULL) {
1343 		/* terminate the filter part */
1344 		*q++ = '\0';
1345 	}
1346 
1347 	if (*p != '\0') {
1348 		/* parse the filter */
1349 		ldap_pvt_hex_unescape (p);
1350 
1351 		if (!*p) {
1352 			/* missing filter */
1353 			LDAP_FREE (url);
1354 			ldap_free_urldesc (ludp);
1355 			return LDAP_URL_ERR_BADFILTER;
1356 		}
1357 
1358 		LDAP_FREE (ludp->lud_filter);
1359 		ludp->lud_filter = LDAP_STRDUP (p);
1360 
1361 		if (ludp->lud_filter == NULL) {
1362 			LDAP_FREE (url);
1363 			ldap_free_urldesc (ludp);
1364 			return LDAP_URL_ERR_MEM;
1365 		}
1366 	}
1367 
1368 	if (q == NULL) {
1369 		/* no more */
1370 		LDAP_FREE (url);
1371 		*ludpp = ludp;
1372 		return LDAP_URL_SUCCESS;
1373 	}
1374 
1375 	/* scan forward for '?' that may marks end of extensions */
1376 	p = q;
1377 	q = strchr (p, '?');
1378 
1379 	if (q != NULL) {
1380 		/* extra '?' */
1381 		LDAP_FREE (url);
1382 		ldap_free_urldesc (ludp);
1383 		return LDAP_URL_ERR_BADURL;
1384 	}
1385 
1386 	/* parse the extensions */
1387 	ludp->lud_exts = ldap_str2charray (p, ",");
1388 
1389 	if (ludp->lud_exts == NULL) {
1390 		LDAP_FREE (url);
1391 		ldap_free_urldesc (ludp);
1392 		return LDAP_URL_ERR_BADEXTS;
1393 	}
1394 
1395 	for (i = 0; ludp->lud_exts[i] != NULL; i++) {
1396 		ldap_pvt_hex_unescape (ludp->lud_exts[i]);
1397 
1398 		if (*ludp->lud_exts[i] == '!') {
1399 			/* count the number of critical extensions */
1400 			ludp->lud_crit_exts++;
1401 		}
1402 	}
1403 
1404 	if (i == 0) {
1405 		/* must have 1 or more */
1406 		LDAP_FREE (url);
1407 		ldap_free_urldesc (ludp);
1408 		return LDAP_URL_ERR_BADEXTS;
1409 	}
1410 
1411 	/* no more */
1412 	*ludpp = ludp;
1413 	LDAP_FREE (url);
1414 	return LDAP_URL_SUCCESS;
1415 }
1416 
1417 static gint
ldap_url_parse(LDAP_CONST gchar * url_in,LDAPURLDesc ** ludpp)1418 ldap_url_parse (LDAP_CONST gchar *url_in,
1419                 LDAPURLDesc **ludpp)
1420 {
1421 	gint rc = ldap_url_parse_ext (url_in, ludpp);
1422 
1423 	if (rc != LDAP_URL_SUCCESS) {
1424 		return rc;
1425 	}
1426 
1427 	if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
1428 		(*ludpp)->lud_scope = LDAP_SCOPE_BASE;
1429 	}
1430 
1431 	if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
1432 		LDAP_FREE ((*ludpp)->lud_host);
1433 		(*ludpp)->lud_host = NULL;
1434 	}
1435 
1436 	if ((*ludpp)->lud_port == 0) {
1437 		if (strcmp ((*ludpp)->lud_scheme, "ldap") == 0) {
1438 			(*ludpp)->lud_port = LDAP_PORT;
1439 #ifdef LDAP_CONNECTIONLESS
1440 		} else if (strcmp ((*ludpp)->lud_scheme, "cldap") == 0) {
1441 			(*ludpp)->lud_port = LDAP_PORT;
1442 #endif
1443 		} else if (strcmp ((*ludpp)->lud_scheme, "ldaps") == 0) {
1444 			(*ludpp)->lud_port = LDAPS_PORT;
1445 		}
1446 	}
1447 
1448 	return rc;
1449 }
1450 
1451