1450 lines
31 KiB
C
1450 lines
31 KiB
C
/* This is extracted from the OpenLDAP sources.
|
|
*
|
|
* Stuff that isn't used in e-book-backend-ldap.c was dropped, like
|
|
* the LDAPSchemaExtensionItem stuff.
|
|
*
|
|
* This file basically has three parts:
|
|
*
|
|
* - some general macros from OpenLDAP that work as such on all
|
|
* implementations.
|
|
*
|
|
* - ldap_str2objectclass()
|
|
*
|
|
* - ldap_url_parse()
|
|
*/
|
|
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 1998-2005 The OpenLDAP Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in file COPYING.OPENLDAP in
|
|
* the top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
/* from various header files */
|
|
|
|
#define LDAP_CONST const
|
|
|
|
#define LDAP_PORT 389 /* ldap:/// default LDAP port */
|
|
#define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */
|
|
|
|
#define LDAP_ROOT_DSE ""
|
|
|
|
#define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
|
|
#define LDAP_DIGIT(c) ((c) >= '0' && (c) <= '9')
|
|
|
|
#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" /* RFC 2830 */
|
|
|
|
#define LDAP_MALLOC(n) malloc((n))
|
|
#define LDAP_CALLOC(n,s) calloc((n),(s))
|
|
#define LDAP_REALLOC(p,s) realloc((p),(s))
|
|
#define LDAP_FREE(p) free((p))
|
|
#define LDAP_VFREE(p) vfree((gpointer *)(p))
|
|
#define LDAP_STRDUP(s) strdup((s))
|
|
|
|
#define LDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y)))
|
|
#define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
|
|
|
|
#define ldap_msgtype(lm) (lm)->lm_msgtype
|
|
#define ldap_msgid(lm) (lm)->lm_msgid
|
|
#ifndef LDAP_TYPE_OR_VALUE_EXISTS
|
|
#define LDAP_TYPE_OR_VALUE_EXISTS 0x14
|
|
#endif
|
|
#ifndef LDAP_SCOPE_DEFAULT
|
|
#define LDAP_SCOPE_DEFAULT -1
|
|
#endif
|
|
#ifndef LDAP_OPT_SUCCESS
|
|
#define LDAP_OPT_SUCCESS 0x00
|
|
#endif
|
|
#ifndef LDAP_INSUFFICIENT_ACCESS
|
|
#define LDAP_INSUFFICIENT_ACCESS 0x32
|
|
#endif
|
|
|
|
#define LDAP_SCHERR_OUTOFMEM 1
|
|
#define LDAP_SCHERR_UNEXPTOKEN 2
|
|
#define LDAP_SCHERR_NOLEFTPAREN 3
|
|
#define LDAP_SCHERR_NORIGHTPAREN 4
|
|
#define LDAP_SCHERR_NODIGIT 5
|
|
#define LDAP_SCHERR_BADNAME 6
|
|
#define LDAP_SCHERR_BADDESC 7
|
|
#define LDAP_SCHERR_BADSUP 8
|
|
#define LDAP_SCHERR_DUPOPT 9
|
|
#define LDAP_SCHERR_EMPTY 10
|
|
#define LDAP_SCHERR_MISSING 11
|
|
#define LDAP_SCHERR_OUT_OF_ORDER 12
|
|
|
|
#define LDAP_SCHEMA_YES 1
|
|
|
|
#define LDAP_SCHEMA_ABSTRACT 0
|
|
#define LDAP_SCHEMA_STRUCTURAL 1
|
|
#define LDAP_SCHEMA_AUXILIARY 2
|
|
|
|
#define LDAP_SCHEMA_ALLOW_NONE 0x00U /* Strict parsing */
|
|
#define LDAP_SCHEMA_ALLOW_NO_OID 0x01U /* Allow missing oid */
|
|
#define LDAP_SCHEMA_ALLOW_QUOTED 0x02U /* Allow bogus extra quotes */
|
|
#define LDAP_SCHEMA_ALLOW_DESCR 0x04U /* Allow descr instead of OID */
|
|
#define LDAP_SCHEMA_ALLOW_DESCR_PREFIX 0x08U /* Allow descr as OID prefix */
|
|
#define LDAP_SCHEMA_ALLOW_OID_MACRO 0x10U /* Allow OID macros in slapd */
|
|
#define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */
|
|
#define LDAP_SCHEMA_ALLOW_ALL 0x3fU /* Be very liberal in parsing */
|
|
#define LDAP_SCHEMA_SKIP 0x80U /* Don't malloc any result */
|
|
|
|
typedef struct ldap_objectclass {
|
|
gchar *oc_oid; /* REQUIRED */
|
|
gchar **oc_names; /* OPTIONAL */
|
|
gchar *oc_desc; /* OPTIONAL */
|
|
gint oc_obsolete; /* 0=no, 1=yes */
|
|
gchar **oc_sup_oids; /* OPTIONAL */
|
|
gint oc_kind; /* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */
|
|
gchar **oc_at_oids_must; /* OPTIONAL */
|
|
gchar **oc_at_oids_may; /* OPTIONAL */
|
|
} LDAPObjectClass;
|
|
|
|
static void
|
|
vfree (gpointer *vec)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; vec[i] != NULL; i++)
|
|
free (vec[i]);
|
|
}
|
|
|
|
/* from schema.c */
|
|
|
|
/*
|
|
* Now come the parsers. There is one parser for each entity type:
|
|
* objectclasses, attributetypes, etc.
|
|
*
|
|
* Each of them is written as a recursive-descent parser, except that
|
|
* none of them is really recursive. But the idea is kept: there
|
|
* is one routine per non-terminal that eithers gobbles lexical tokens
|
|
* or calls lower-level routines, etc.
|
|
*
|
|
* The scanner is implemented in the routine get_token. Actually,
|
|
* get_token is more than a scanner and will return tokens that are
|
|
* in fact non-terminals in the grammar. So you can see the whole
|
|
* approach as the combination of a low-level bottom-up recognizer
|
|
* combined with a scanner and a number of top-down parsers. Or just
|
|
* consider that the real grammars recognized by the parsers are not
|
|
* those of the standards. As a matter of fact, our parsers are more
|
|
* liberal than the spec when there is no ambiguity.
|
|
*
|
|
* The difference is pretty academic (modulo bugs or incorrect
|
|
* interpretation of the specs).
|
|
*/
|
|
|
|
#define TK_NOENDQUOTE -2
|
|
#define TK_OUTOFMEM -1
|
|
#define TK_EOS 0
|
|
#define TK_UNEXPCHAR 1
|
|
#define TK_BAREWORD 2
|
|
#define TK_QDSTRING 3
|
|
#define TK_LEFTPAREN 4
|
|
#define TK_RIGHTPAREN 5
|
|
#define TK_DOLLAR 6
|
|
#define TK_QDESCR TK_QDSTRING
|
|
|
|
struct token {
|
|
gint type;
|
|
gchar *sval;
|
|
};
|
|
|
|
static gint
|
|
get_token (const gchar **sp,
|
|
gchar **token_val)
|
|
{
|
|
gint kind;
|
|
const gchar *p;
|
|
const gchar *q;
|
|
gchar *res;
|
|
|
|
*token_val = NULL;
|
|
switch (**sp) {
|
|
case '\0':
|
|
kind = TK_EOS;
|
|
(*sp)++;
|
|
break;
|
|
case '(':
|
|
kind = TK_LEFTPAREN;
|
|
(*sp)++;
|
|
break;
|
|
case ')':
|
|
kind = TK_RIGHTPAREN;
|
|
(*sp)++;
|
|
break;
|
|
case '$':
|
|
kind = TK_DOLLAR;
|
|
(*sp)++;
|
|
break;
|
|
case '\'':
|
|
kind = TK_QDSTRING;
|
|
(*sp)++;
|
|
p = *sp;
|
|
while (**sp != '\'' && **sp != '\0')
|
|
(*sp)++;
|
|
if (**sp == '\'') {
|
|
q = *sp;
|
|
res = LDAP_MALLOC (q-p+1);
|
|
if (!res) {
|
|
kind = TK_OUTOFMEM;
|
|
} else {
|
|
strncpy (res,p,q-p);
|
|
res[q-p] = '\0';
|
|
*token_val = res;
|
|
}
|
|
(*sp)++;
|
|
} else {
|
|
kind = TK_NOENDQUOTE;
|
|
}
|
|
break;
|
|
default:
|
|
kind = TK_BAREWORD;
|
|
p = *sp;
|
|
while (!LDAP_SPACE (**sp) &&
|
|
**sp != '(' &&
|
|
**sp != ')' &&
|
|
**sp != '$' &&
|
|
**sp != '\'' &&
|
|
**sp != '\0')
|
|
(*sp)++;
|
|
q = *sp;
|
|
res = LDAP_MALLOC (q-p+1);
|
|
if (!res) {
|
|
kind = TK_OUTOFMEM;
|
|
} else {
|
|
strncpy (res,p,q-p);
|
|
res[q-p] = '\0';
|
|
*token_val = res;
|
|
}
|
|
break;
|
|
/* kind = TK_UNEXPCHAR; */
|
|
/* break; */
|
|
}
|
|
|
|
return kind;
|
|
}
|
|
|
|
/* Gobble optional whitespace */
|
|
static void
|
|
parse_whsp (const gchar **sp)
|
|
{
|
|
while (LDAP_SPACE (**sp))
|
|
(*sp)++;
|
|
}
|
|
|
|
/* Parse a sequence of dot-separated decimal strings */
|
|
static gchar *
|
|
ldap_int_parse_numericoid (const gchar **sp, gint *code, const gint flags)
|
|
{
|
|
gchar *res = NULL;
|
|
const gchar *start = *sp;
|
|
gint len;
|
|
gint quoted = 0;
|
|
|
|
/* Netscape puts the SYNTAX value in quotes (incorrectly) */
|
|
if (flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'') {
|
|
quoted = 1;
|
|
(*sp)++;
|
|
start++;
|
|
}
|
|
/* Each iteration of this loop gets one decimal string */
|
|
while (**sp) {
|
|
if (!LDAP_DIGIT (**sp)) {
|
|
/*
|
|
* Initial gchar is not a digit or gchar after dot is
|
|
* not a digit
|
|
*/
|
|
*code = LDAP_SCHERR_NODIGIT;
|
|
return NULL;
|
|
}
|
|
(*sp)++;
|
|
while (LDAP_DIGIT (**sp))
|
|
(*sp)++;
|
|
if (**sp != '.')
|
|
break;
|
|
/* Otherwise, gobble the dot and loop again */
|
|
(*sp)++;
|
|
}
|
|
/* Now *sp points at the gchar past the numericoid. Perfect. */
|
|
len = *sp - start;
|
|
if (flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted) {
|
|
if (**sp == '\'') {
|
|
(*sp)++;
|
|
} else {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
return NULL;
|
|
}
|
|
}
|
|
if (flags & LDAP_SCHEMA_SKIP) {
|
|
res = (gchar *) start;
|
|
} else {
|
|
res = LDAP_MALLOC (len+1);
|
|
if (!res) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return (NULL);
|
|
}
|
|
strncpy (res,start,len);
|
|
res[len] = '\0';
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
/* Parse a qdescr or a list of them enclosed in () */
|
|
static gchar **
|
|
parse_qdescrs (const gchar **sp, gint *code)
|
|
{
|
|
gchar ** res;
|
|
gchar ** res1;
|
|
gint kind;
|
|
gchar *sval;
|
|
gint size;
|
|
gint pos;
|
|
|
|
parse_whsp (sp);
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_LEFTPAREN) {
|
|
/* Let's presume there will be at least 2 entries */
|
|
size = 3;
|
|
res = LDAP_CALLOC (3,sizeof (gchar *));
|
|
if (!res) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return NULL;
|
|
}
|
|
pos = 0;
|
|
while (1) {
|
|
parse_whsp (sp);
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_RIGHTPAREN)
|
|
break;
|
|
if (kind == TK_QDESCR) {
|
|
if (pos == size-2) {
|
|
size++;
|
|
res1 = LDAP_REALLOC (res,size*sizeof (gchar *));
|
|
if (!res1) {
|
|
LDAP_VFREE (res);
|
|
LDAP_FREE (sval);
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return (NULL);
|
|
}
|
|
res = res1;
|
|
}
|
|
res[pos++] = sval;
|
|
res[pos] = NULL;
|
|
parse_whsp (sp);
|
|
} else {
|
|
LDAP_VFREE (res);
|
|
LDAP_FREE (sval);
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
return (NULL);
|
|
}
|
|
}
|
|
parse_whsp (sp);
|
|
return (res);
|
|
} else if (kind == TK_QDESCR) {
|
|
res = LDAP_CALLOC (2,sizeof (gchar *));
|
|
if (!res) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return NULL;
|
|
}
|
|
res[0] = sval;
|
|
res[1] = NULL;
|
|
parse_whsp (sp);
|
|
return res;
|
|
} else {
|
|
LDAP_FREE (sval);
|
|
*code = LDAP_SCHERR_BADNAME;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Parse a woid or a $-separated list of them enclosed in () */
|
|
static gchar **
|
|
parse_oids (const gchar **sp, gint *code, const gint allow_quoted)
|
|
{
|
|
gchar ** res;
|
|
gchar ** res1;
|
|
gint kind;
|
|
gchar *sval;
|
|
gint size;
|
|
gint pos;
|
|
|
|
/*
|
|
* Strictly speaking, doing this here accepts whsp before the
|
|
* ( at the begining of an oidlist, but this is harmless. Also,
|
|
* we are very liberal in what we accept as an OID. Maybe
|
|
* refine later.
|
|
*/
|
|
parse_whsp (sp);
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_LEFTPAREN) {
|
|
/* Let's presume there will be at least 2 entries */
|
|
size = 3;
|
|
res = LDAP_CALLOC (3,sizeof (gchar *));
|
|
if (!res) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return NULL;
|
|
}
|
|
pos = 0;
|
|
parse_whsp (sp);
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_BAREWORD ||
|
|
(allow_quoted && kind == TK_QDSTRING)) {
|
|
res[pos++] = sval;
|
|
res[pos] = NULL;
|
|
} else {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
LDAP_FREE (sval);
|
|
LDAP_VFREE (res);
|
|
return NULL;
|
|
}
|
|
parse_whsp (sp);
|
|
while (1) {
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_RIGHTPAREN)
|
|
break;
|
|
if (kind == TK_DOLLAR) {
|
|
parse_whsp (sp);
|
|
kind = get_token (sp,&sval);
|
|
if (kind == TK_BAREWORD ||
|
|
(allow_quoted &&
|
|
kind == TK_QDSTRING)) {
|
|
if (pos == size-2) {
|
|
size++;
|
|
res1 = LDAP_REALLOC (res,size*sizeof (gchar *));
|
|
if (!res1) {
|
|
LDAP_FREE (sval);
|
|
LDAP_VFREE (res);
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return (NULL);
|
|
}
|
|
res = res1;
|
|
}
|
|
res[pos++] = sval;
|
|
res[pos] = NULL;
|
|
} else {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
LDAP_FREE (sval);
|
|
LDAP_VFREE (res);
|
|
return NULL;
|
|
}
|
|
parse_whsp (sp);
|
|
} else {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
LDAP_FREE (sval);
|
|
LDAP_VFREE (res);
|
|
return NULL;
|
|
}
|
|
}
|
|
parse_whsp (sp);
|
|
return (res);
|
|
} else if (kind == TK_BAREWORD ||
|
|
(allow_quoted && kind == TK_QDSTRING)) {
|
|
res = LDAP_CALLOC (2,sizeof (gchar *));
|
|
if (!res) {
|
|
LDAP_FREE (sval);
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return NULL;
|
|
}
|
|
res[0] = sval;
|
|
res[1] = NULL;
|
|
parse_whsp (sp);
|
|
return res;
|
|
} else {
|
|
LDAP_FREE (sval);
|
|
*code = LDAP_SCHERR_BADNAME;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ldap_objectclass_free (LDAPObjectClass *oc)
|
|
{
|
|
LDAP_FREE (oc->oc_oid);
|
|
if (oc->oc_names) LDAP_VFREE (oc->oc_names);
|
|
if (oc->oc_desc) LDAP_FREE (oc->oc_desc);
|
|
if (oc->oc_sup_oids) LDAP_VFREE (oc->oc_sup_oids);
|
|
if (oc->oc_at_oids_must) LDAP_VFREE (oc->oc_at_oids_must);
|
|
if (oc->oc_at_oids_may) LDAP_VFREE (oc->oc_at_oids_may);
|
|
LDAP_FREE (oc);
|
|
}
|
|
|
|
static LDAPObjectClass *
|
|
ldap_str2objectclass (LDAP_CONST gchar *s,
|
|
gint *code,
|
|
LDAP_CONST gchar **errp,
|
|
LDAP_CONST unsigned flags)
|
|
{
|
|
gint kind;
|
|
const gchar *ss = s;
|
|
gchar *sval;
|
|
gint seen_name = 0;
|
|
gint seen_desc = 0;
|
|
gint seen_obsolete = 0;
|
|
gint seen_sup = 0;
|
|
gint seen_kind = 0;
|
|
gint seen_must = 0;
|
|
gint seen_may = 0;
|
|
LDAPObjectClass *oc;
|
|
gchar ** ext_vals;
|
|
const gchar *savepos;
|
|
|
|
if (!s) {
|
|
*code = LDAP_SCHERR_EMPTY;
|
|
*errp = "";
|
|
return NULL;
|
|
}
|
|
|
|
*errp = s;
|
|
oc = LDAP_CALLOC (1,sizeof (LDAPObjectClass));
|
|
|
|
if (!oc) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
return NULL;
|
|
}
|
|
oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
|
|
|
|
kind = get_token (&ss,&sval);
|
|
if (kind != TK_LEFTPAREN) {
|
|
*code = LDAP_SCHERR_NOLEFTPAREN;
|
|
LDAP_FREE (sval);
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Definitions MUST begin with an OID in the numericoid format.
|
|
* However, this routine is used by clients to parse the response
|
|
* from servers and very well known servers will provide an OID
|
|
* in the wrong format or even no OID at all. We do our best to
|
|
* extract info from those servers.
|
|
*/
|
|
parse_whsp (&ss);
|
|
savepos = ss;
|
|
oc->oc_oid = ldap_int_parse_numericoid (&ss,code,0);
|
|
if (!oc->oc_oid) {
|
|
if ((flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos)) {
|
|
/* Backtracking */
|
|
ss = savepos;
|
|
kind = get_token (&ss,&sval);
|
|
if (kind == TK_BAREWORD) {
|
|
if (!strcasecmp (sval, "NAME") ||
|
|
!strcasecmp (sval, "DESC") ||
|
|
!strcasecmp (sval, "OBSOLETE") ||
|
|
!strcasecmp (sval, "SUP") ||
|
|
!strcasecmp (sval, "ABSTRACT") ||
|
|
!strcasecmp (sval, "STRUCTURAL") ||
|
|
!strcasecmp (sval, "AUXILIARY") ||
|
|
!strcasecmp (sval, "MUST") ||
|
|
!strcasecmp (sval, "MAY") ||
|
|
!strncasecmp (sval, "X-", 2)) {
|
|
/* Missing OID, backtrack */
|
|
ss = savepos;
|
|
} else if (flags &
|
|
LDAP_SCHEMA_ALLOW_OID_MACRO) {
|
|
/* Non-numerical OID, ignore */
|
|
gint len = ss-savepos;
|
|
oc->oc_oid = LDAP_MALLOC (len+1);
|
|
strncpy (oc->oc_oid, savepos, len);
|
|
oc->oc_oid[len] = 0;
|
|
}
|
|
}
|
|
LDAP_FREE (sval);
|
|
} else {
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
}
|
|
parse_whsp (&ss);
|
|
|
|
/*
|
|
* Beyond this point we will be liberal an accept the items
|
|
* in any order.
|
|
*/
|
|
while (1) {
|
|
kind = get_token (&ss,&sval);
|
|
switch (kind) {
|
|
case TK_EOS:
|
|
*code = LDAP_SCHERR_NORIGHTPAREN;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
case TK_RIGHTPAREN:
|
|
return oc;
|
|
case TK_BAREWORD:
|
|
if (!strcasecmp (sval,"NAME")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_name) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_name = 1;
|
|
oc->oc_names = parse_qdescrs (&ss,code);
|
|
if (!oc->oc_names) {
|
|
if (*code != LDAP_SCHERR_OUTOFMEM)
|
|
*code = LDAP_SCHERR_BADNAME;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
} else if (!strcasecmp (sval,"DESC")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_desc) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_desc = 1;
|
|
parse_whsp (&ss);
|
|
kind = get_token (&ss,&sval);
|
|
if (kind != TK_QDSTRING) {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
*errp = ss;
|
|
LDAP_FREE (sval);
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
oc->oc_desc = sval;
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"OBSOLETE")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_obsolete) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_obsolete = 1;
|
|
oc->oc_obsolete = LDAP_SCHEMA_YES;
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"SUP")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_sup) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_sup = 1;
|
|
oc->oc_sup_oids = parse_oids (&ss,
|
|
code,
|
|
flags);
|
|
if (!oc->oc_sup_oids) {
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
} else if (!strcasecmp (sval,"ABSTRACT")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_kind) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_kind = 1;
|
|
oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"STRUCTURAL")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_kind) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_kind = 1;
|
|
oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"AUXILIARY")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_kind) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_kind = 1;
|
|
oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"MUST")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_must) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_must = 1;
|
|
oc->oc_at_oids_must = parse_oids (&ss,code,0);
|
|
if (!oc->oc_at_oids_must) {
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
parse_whsp (&ss);
|
|
} else if (!strcasecmp (sval,"MAY")) {
|
|
LDAP_FREE (sval);
|
|
if (seen_may) {
|
|
*code = LDAP_SCHERR_DUPOPT;
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return (NULL);
|
|
}
|
|
seen_may = 1;
|
|
oc->oc_at_oids_may = parse_oids (&ss,code,0);
|
|
if (!oc->oc_at_oids_may) {
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
parse_whsp (&ss);
|
|
} else if (sval[0] == 'X' && sval[1] == '-') {
|
|
/* Should be parse_qdstrings */
|
|
ext_vals = parse_qdescrs (&ss, code);
|
|
if (!ext_vals) {
|
|
*errp = ss;
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
#if 0
|
|
if (add_extension (&oc->oc_extensions,
|
|
sval, ext_vals)) {
|
|
*code = LDAP_SCHERR_OUTOFMEM;
|
|
*errp = ss;
|
|
LDAP_FREE (sval);
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
} else {
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
*errp = ss;
|
|
LDAP_FREE (sval);
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
break;
|
|
default:
|
|
*code = LDAP_SCHERR_UNEXPTOKEN;
|
|
*errp = ss;
|
|
LDAP_FREE (sval);
|
|
ldap_objectclass_free (oc);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* from utf-8.c */
|
|
|
|
#define LDAP_UTF8_NEXT(p) g_utf8_next_char((p))
|
|
#define LDAP_UTF8_INCR(p) ((p)=LDAP_UTF8_NEXT((p)))
|
|
#define ldap_x_utf8_to_ucs4(str) g_utf8_get_char(str)
|
|
|
|
static gchar *
|
|
ldap_utf8_strchr (const gchar *str,
|
|
const gchar *chr)
|
|
{
|
|
for (; *str != '\0'; LDAP_UTF8_INCR (str)) {
|
|
if (ldap_x_utf8_to_ucs4 (str) == ldap_x_utf8_to_ucs4 (chr)) {
|
|
return (gchar *) str;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gsize
|
|
ldap_utf8_strcspn (const gchar *str,
|
|
const gchar *set)
|
|
{
|
|
const gchar *cstr;
|
|
const gchar *cset;
|
|
|
|
for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) {
|
|
for (cset = set; *cset != '\0'; LDAP_UTF8_INCR (cset)) {
|
|
if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) {
|
|
return cstr - str;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cstr - str;
|
|
}
|
|
|
|
static gsize
|
|
ldap_utf8_strspn (const gchar *str,
|
|
const gchar *set)
|
|
{
|
|
const gchar *cstr;
|
|
const gchar *cset;
|
|
|
|
for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) {
|
|
for (cset = set; ; LDAP_UTF8_INCR (cset)) {
|
|
if (*cset == '\0') {
|
|
return cstr - str;
|
|
}
|
|
|
|
if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cstr - str;
|
|
}
|
|
|
|
static gchar *ldap_utf8_strtok (gchar *str, const gchar *sep, gchar **last)
|
|
{
|
|
gchar *begin;
|
|
gchar *end;
|
|
|
|
if (last == NULL) return NULL;
|
|
|
|
begin = str ? str : *last;
|
|
|
|
begin += ldap_utf8_strspn (begin, sep);
|
|
|
|
if (*begin == '\0') {
|
|
*last = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
end = &begin[ ldap_utf8_strcspn (begin, sep) ];
|
|
|
|
if (*end != '\0') {
|
|
gchar *next = LDAP_UTF8_NEXT (end);
|
|
*end = '\0';
|
|
end = next;
|
|
}
|
|
|
|
*last = end;
|
|
return begin;
|
|
}
|
|
|
|
/* from ldap.h */
|
|
|
|
#define LDAP_URL_SUCCESS 0x00 /* Success */
|
|
#define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */
|
|
#define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */
|
|
|
|
#define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */
|
|
#define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */
|
|
#define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */
|
|
#define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */
|
|
#define LDAP_URL_ERR_BADATTRS 0x07 /* bad (or missing) attributes */
|
|
#define LDAP_URL_ERR_BADSCOPE 0x08 /* scope string is invalid (or missing) */
|
|
#define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */
|
|
#define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */
|
|
|
|
#define LDAP_URL_PREFIX "ldap://"
|
|
#define LDAP_URL_PREFIX_LEN (sizeof(LDAP_URL_PREFIX)-1)
|
|
#define LDAPS_URL_PREFIX "ldaps://"
|
|
#define LDAPS_URL_PREFIX_LEN (sizeof(LDAPS_URL_PREFIX)-1)
|
|
#define LDAPI_URL_PREFIX "ldapi://"
|
|
#define LDAPI_URL_PREFIX_LEN (sizeof(LDAPI_URL_PREFIX)-1)
|
|
|
|
#define LDAP_URL_URLCOLON "URL:"
|
|
#define LDAP_URL_URLCOLON_LEN (sizeof(LDAP_URL_URLCOLON)-1)
|
|
|
|
typedef struct ldap_url_desc {
|
|
struct ldap_url_desc *lud_next;
|
|
gchar *lud_scheme;
|
|
gchar *lud_host;
|
|
gint lud_port;
|
|
gchar *lud_dn;
|
|
gchar **lud_attrs;
|
|
gint lud_scope;
|
|
gchar *lud_filter;
|
|
gchar **lud_exts;
|
|
gint lud_crit_exts;
|
|
} LDAPURLDesc;
|
|
|
|
/* from url.c */
|
|
|
|
static const gchar *
|
|
skip_url_prefix (
|
|
const gchar *url,
|
|
gint *enclosedp,
|
|
const gchar **scheme)
|
|
{
|
|
/*
|
|
* return non-zero if this looks like a LDAP URL; zero if not
|
|
* if non-zero returned, *urlp will be moved past "ldap://" part of URL
|
|
*/
|
|
const gchar *p;
|
|
|
|
if (url == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
p = url;
|
|
|
|
/* skip leading '<' (if any) */
|
|
if (*p == '<') {
|
|
*enclosedp = 1;
|
|
++p;
|
|
} else {
|
|
*enclosedp = 0;
|
|
}
|
|
|
|
/* skip leading "URL:" (if any) */
|
|
if (strncasecmp (p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN) == 0) {
|
|
p += LDAP_URL_URLCOLON_LEN;
|
|
}
|
|
|
|
/* check for "ldap://" prefix */
|
|
if (strncasecmp (p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN) == 0) {
|
|
/* skip over "ldap://" prefix and return success */
|
|
p += LDAP_URL_PREFIX_LEN;
|
|
*scheme = "ldap";
|
|
return (p);
|
|
}
|
|
|
|
/* check for "ldaps://" prefix */
|
|
if (strncasecmp (p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN) == 0) {
|
|
/* skip over "ldaps://" prefix and return success */
|
|
p += LDAPS_URL_PREFIX_LEN;
|
|
*scheme = "ldaps";
|
|
return (p);
|
|
}
|
|
|
|
/* check for "ldapi://" prefix */
|
|
if (strncasecmp (p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN) == 0) {
|
|
/* skip over "ldapi://" prefix and return success */
|
|
p += LDAPI_URL_PREFIX_LEN;
|
|
*scheme = "ldapi";
|
|
return (p);
|
|
}
|
|
|
|
#ifdef LDAP_CONNECTIONLESS
|
|
/* check for "cldap://" prefix */
|
|
if (strncasecmp (p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN) == 0) {
|
|
/* skip over "cldap://" prefix and return success */
|
|
p += LDAPC_URL_PREFIX_LEN;
|
|
*scheme = "cldap";
|
|
return (p);
|
|
}
|
|
#endif
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
static gint
|
|
str2scope (const gchar *p)
|
|
{
|
|
if (strcasecmp (p, "one") == 0) {
|
|
return LDAP_SCOPE_ONELEVEL;
|
|
|
|
} else if (strcasecmp (p, "onelevel") == 0) {
|
|
return LDAP_SCOPE_ONELEVEL;
|
|
|
|
} else if (strcasecmp (p, "base") == 0) {
|
|
return LDAP_SCOPE_BASE;
|
|
|
|
} else if (strcasecmp (p, "sub") == 0) {
|
|
return LDAP_SCOPE_SUBTREE;
|
|
|
|
} else if (strcasecmp (p, "subtree") == 0) {
|
|
return LDAP_SCOPE_SUBTREE;
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
static void
|
|
ldap_free_urldesc (LDAPURLDesc *ludp)
|
|
{
|
|
if (ludp == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (ludp->lud_scheme != NULL) {
|
|
LDAP_FREE (ludp->lud_scheme);
|
|
}
|
|
|
|
if (ludp->lud_host != NULL) {
|
|
LDAP_FREE (ludp->lud_host);
|
|
}
|
|
|
|
if (ludp->lud_dn != NULL) {
|
|
LDAP_FREE (ludp->lud_dn);
|
|
}
|
|
|
|
if (ludp->lud_filter != NULL) {
|
|
LDAP_FREE (ludp->lud_filter);
|
|
}
|
|
|
|
if (ludp->lud_attrs != NULL) {
|
|
LDAP_VFREE (ludp->lud_attrs);
|
|
}
|
|
|
|
if (ludp->lud_exts != NULL) {
|
|
LDAP_VFREE (ludp->lud_exts);
|
|
}
|
|
|
|
LDAP_FREE (ludp);
|
|
}
|
|
|
|
static gint
|
|
ldap_int_unhex (gint c)
|
|
{
|
|
return (c >= '0' && c <= '9' ? c - '0'
|
|
: c >= 'A' && c <= 'F' ? c - 'A' + 10
|
|
: c - 'a' + 10);
|
|
}
|
|
|
|
static void
|
|
ldap_pvt_hex_unescape (gchar *s)
|
|
{
|
|
/*
|
|
* Remove URL hex escapes from s... done in place. The basic concept for
|
|
* this routine is borrowed from the WWW library HTUnEscape() routine.
|
|
*/
|
|
gchar *p;
|
|
|
|
for (p = s; *s != '\0'; ++s) {
|
|
if (*s == '%') {
|
|
if (*++s == '\0') {
|
|
break;
|
|
}
|
|
*p = ldap_int_unhex(*s) << 4;
|
|
if (*++s == '\0') {
|
|
break;
|
|
}
|
|
*p++ += ldap_int_unhex(*s);
|
|
} else {
|
|
*p++ = *s;
|
|
}
|
|
}
|
|
|
|
*p = '\0';
|
|
}
|
|
|
|
static gchar **
|
|
ldap_str2charray (const gchar *str_in,
|
|
const gchar *brkstr)
|
|
{
|
|
gchar **res;
|
|
gchar *str, *s;
|
|
gchar *lasts;
|
|
gint i;
|
|
|
|
/* protect the input string from strtok */
|
|
str = LDAP_STRDUP (str_in);
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
i = 1;
|
|
for (s = str; *s; s++) {
|
|
if (ldap_utf8_strchr (brkstr, s) != NULL) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
res = (gchar **) LDAP_MALLOC ((i + 1) * sizeof (gchar *));
|
|
|
|
if (res == NULL) {
|
|
LDAP_FREE (str);
|
|
return NULL;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
for (s = ldap_utf8_strtok (str, brkstr, &lasts);
|
|
s != NULL;
|
|
s = ldap_utf8_strtok (NULL, brkstr, &lasts))
|
|
{
|
|
res[i] = LDAP_STRDUP (s);
|
|
|
|
if (res[i] == NULL) {
|
|
for (--i; i >= 0; i--) {
|
|
LDAP_FREE (res[i]);
|
|
}
|
|
LDAP_FREE (res);
|
|
LDAP_FREE (str);
|
|
return NULL;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
res[i] = NULL;
|
|
|
|
LDAP_FREE (str);
|
|
return (res);
|
|
}
|
|
|
|
static gint
|
|
ldap_url_parse_ext (LDAP_CONST gchar *url_in,
|
|
LDAPURLDesc **ludpp)
|
|
{
|
|
/*
|
|
* Pick apart the pieces of an LDAP URL.
|
|
*/
|
|
|
|
LDAPURLDesc *ludp;
|
|
gchar *p, *q, *r;
|
|
gint i, enclosed;
|
|
const gchar *scheme = NULL;
|
|
const gchar *url_tmp;
|
|
gchar *url;
|
|
|
|
if (url_in == NULL || ludpp == NULL) {
|
|
return LDAP_URL_ERR_PARAM;
|
|
}
|
|
|
|
*ludpp = NULL; /* pessimistic */
|
|
|
|
url_tmp = skip_url_prefix (url_in, &enclosed, &scheme);
|
|
|
|
if (url_tmp == NULL) {
|
|
return LDAP_URL_ERR_BADSCHEME;
|
|
}
|
|
|
|
assert (scheme);
|
|
|
|
/* make working copy of the remainder of the URL */
|
|
url = LDAP_STRDUP (url_tmp);
|
|
if (url == NULL) {
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
|
|
if (enclosed) {
|
|
p = &url[strlen (url)-1];
|
|
|
|
if (*p != '>') {
|
|
LDAP_FREE (url);
|
|
return LDAP_URL_ERR_BADENCLOSURE;
|
|
}
|
|
|
|
*p = '\0';
|
|
}
|
|
|
|
/* allocate return struct */
|
|
ludp = (LDAPURLDesc *) LDAP_CALLOC (1, sizeof (LDAPURLDesc));
|
|
|
|
if (ludp == NULL) {
|
|
LDAP_FREE (url);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
|
|
ludp->lud_next = NULL;
|
|
ludp->lud_host = NULL;
|
|
ludp->lud_port = 0;
|
|
ludp->lud_dn = NULL;
|
|
ludp->lud_attrs = NULL;
|
|
ludp->lud_filter = NULL;
|
|
ludp->lud_scope = LDAP_SCOPE_DEFAULT;
|
|
ludp->lud_filter = NULL;
|
|
ludp->lud_exts = NULL;
|
|
|
|
ludp->lud_scheme = LDAP_STRDUP (scheme);
|
|
|
|
if (ludp->lud_scheme == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
|
|
/* scan forward for '/' that marks end of hostport and begin. of dn */
|
|
p = strchr (url, '/');
|
|
|
|
if (p != NULL) {
|
|
/* terminate hostport; point to start of dn */
|
|
*p++ = '\0';
|
|
}
|
|
|
|
/* IPv6 syntax with [ip address]:port */
|
|
if (*url == '[') {
|
|
r = strchr (url, ']');
|
|
if (r == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADURL;
|
|
}
|
|
*r++ = '\0';
|
|
q = strchr (r, ':');
|
|
} else {
|
|
q = strchr (url, ':');
|
|
}
|
|
|
|
if (q != NULL) {
|
|
gchar *next;
|
|
|
|
*q++ = '\0';
|
|
ldap_pvt_hex_unescape (q);
|
|
|
|
if (*q == '\0') {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADURL;
|
|
}
|
|
|
|
ludp->lud_port = strtol (q, &next, 10);
|
|
if (next == NULL || next[0] != '\0') {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADURL;
|
|
}
|
|
}
|
|
|
|
ldap_pvt_hex_unescape (url);
|
|
|
|
/* If [ip address]:port syntax, url is [ip and we skip the [ */
|
|
ludp->lud_host = LDAP_STRDUP (url + (*url == '['));
|
|
|
|
if (ludp->lud_host == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
|
|
/*
|
|
* Kludge. ldap://111.222.333.444:389??cn=abc,o=company
|
|
*
|
|
* On early Novell releases, search references/referrals were returned
|
|
* in this format, i.e., the dn was kind of in the scope position,
|
|
* but the required slash is missing. The whole thing is illegal syntax,
|
|
* but we need to account for it. Fortunately it can't be confused with
|
|
* anything real.
|
|
*/
|
|
if ((p == NULL) && (q != NULL) && ((q = strchr (q, '?')) != NULL)) {
|
|
q++;
|
|
/* ? immediately followed by question */
|
|
if (*q == '?') {
|
|
q++;
|
|
if (*q != '\0') {
|
|
/* parse dn part */
|
|
ldap_pvt_hex_unescape (q);
|
|
ludp->lud_dn = LDAP_STRDUP (q);
|
|
} else {
|
|
ludp->lud_dn = LDAP_STRDUP ("");
|
|
}
|
|
|
|
if (ludp->lud_dn == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p == NULL) {
|
|
LDAP_FREE (url);
|
|
*ludpp = ludp;
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
/* scan forward for '?' that may marks end of dn */
|
|
q = strchr (p, '?');
|
|
|
|
if (q != NULL) {
|
|
/* terminate dn part */
|
|
*q++ = '\0';
|
|
}
|
|
|
|
if (*p != '\0') {
|
|
/* parse dn part */
|
|
ldap_pvt_hex_unescape (p);
|
|
ludp->lud_dn = LDAP_STRDUP (p);
|
|
} else {
|
|
ludp->lud_dn = LDAP_STRDUP ("");
|
|
}
|
|
|
|
if (ludp->lud_dn == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
|
|
if (q == NULL) {
|
|
/* no more */
|
|
LDAP_FREE (url);
|
|
*ludpp = ludp;
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
/* scan forward for '?' that may marks end of attributes */
|
|
p = q;
|
|
q = strchr (p, '?');
|
|
|
|
if (q != NULL) {
|
|
/* terminate attributes part */
|
|
*q++ = '\0';
|
|
}
|
|
|
|
if (*p != '\0') {
|
|
/* parse attributes */
|
|
ldap_pvt_hex_unescape (p);
|
|
ludp->lud_attrs = ldap_str2charray (p, ",");
|
|
|
|
if (ludp->lud_attrs == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADATTRS;
|
|
}
|
|
}
|
|
|
|
if (q == NULL) {
|
|
/* no more */
|
|
LDAP_FREE (url);
|
|
*ludpp = ludp;
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
/* scan forward for '?' that may marks end of scope */
|
|
p = q;
|
|
q = strchr (p, '?');
|
|
|
|
if (q != NULL) {
|
|
/* terminate the scope part */
|
|
*q++ = '\0';
|
|
}
|
|
|
|
if (*p != '\0') {
|
|
/* parse the scope */
|
|
ldap_pvt_hex_unescape (p);
|
|
ludp->lud_scope = str2scope (p);
|
|
|
|
if (ludp->lud_scope == -1) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADSCOPE;
|
|
}
|
|
}
|
|
|
|
if (q == NULL) {
|
|
/* no more */
|
|
LDAP_FREE (url);
|
|
*ludpp = ludp;
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
/* scan forward for '?' that may marks end of filter */
|
|
p = q;
|
|
q = strchr (p, '?');
|
|
|
|
if (q != NULL) {
|
|
/* terminate the filter part */
|
|
*q++ = '\0';
|
|
}
|
|
|
|
if (*p != '\0') {
|
|
/* parse the filter */
|
|
ldap_pvt_hex_unescape (p);
|
|
|
|
if (!*p) {
|
|
/* missing filter */
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADFILTER;
|
|
}
|
|
|
|
LDAP_FREE (ludp->lud_filter);
|
|
ludp->lud_filter = LDAP_STRDUP (p);
|
|
|
|
if (ludp->lud_filter == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_MEM;
|
|
}
|
|
}
|
|
|
|
if (q == NULL) {
|
|
/* no more */
|
|
LDAP_FREE (url);
|
|
*ludpp = ludp;
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
/* scan forward for '?' that may marks end of extensions */
|
|
p = q;
|
|
q = strchr (p, '?');
|
|
|
|
if (q != NULL) {
|
|
/* extra '?' */
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADURL;
|
|
}
|
|
|
|
/* parse the extensions */
|
|
ludp->lud_exts = ldap_str2charray (p, ",");
|
|
|
|
if (ludp->lud_exts == NULL) {
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADEXTS;
|
|
}
|
|
|
|
for (i=0; ludp->lud_exts[i] != NULL; i++) {
|
|
ldap_pvt_hex_unescape (ludp->lud_exts[i]);
|
|
|
|
if (*ludp->lud_exts[i] == '!') {
|
|
/* count the number of critical extensions */
|
|
ludp->lud_crit_exts++;
|
|
}
|
|
}
|
|
|
|
if (i == 0) {
|
|
/* must have 1 or more */
|
|
LDAP_FREE (url);
|
|
ldap_free_urldesc (ludp);
|
|
return LDAP_URL_ERR_BADEXTS;
|
|
}
|
|
|
|
/* no more */
|
|
*ludpp = ludp;
|
|
LDAP_FREE (url);
|
|
return LDAP_URL_SUCCESS;
|
|
}
|
|
|
|
static gint
|
|
ldap_url_parse (LDAP_CONST gchar *url_in,
|
|
LDAPURLDesc **ludpp)
|
|
{
|
|
gint rc = ldap_url_parse_ext (url_in, ludpp);
|
|
|
|
if (rc != LDAP_URL_SUCCESS) {
|
|
return rc;
|
|
}
|
|
|
|
if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
|
|
(*ludpp)->lud_scope = LDAP_SCOPE_BASE;
|
|
}
|
|
|
|
if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
|
|
LDAP_FREE ((*ludpp)->lud_host);
|
|
(*ludpp)->lud_host = NULL;
|
|
}
|
|
|
|
if ((*ludpp)->lud_port == 0) {
|
|
if (strcmp ((*ludpp)->lud_scheme, "ldap") == 0) {
|
|
(*ludpp)->lud_port = LDAP_PORT;
|
|
#ifdef LDAP_CONNECTIONLESS
|
|
} else if (strcmp ((*ludpp)->lud_scheme, "cldap") == 0) {
|
|
(*ludpp)->lud_port = LDAP_PORT;
|
|
#endif
|
|
} else if (strcmp ((*ludpp)->lud_scheme, "ldaps") == 0) {
|
|
(*ludpp)->lud_port = LDAPS_PORT;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|