1427 lines
31 KiB
C
1427 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
|
|
|
|
#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;
|
|
}
|
|
|