#pike __REAL_VERSION__ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "ldap_globals.h" |
|
#if constant(SSL.Cipher) |
import SSL.Constants; |
#endif |
|
import "."; |
|
import Standards.ASN1.Types; |
|
|
|
|
|
#define ASN1_GET_RESULTAPP(X) ((X)->elements[1]->get_tag()) |
#define ASN1_GET_DN(X) ((X)->elements[0]->value) |
#define ASN1_GET_ATTR_ARRAY(X) (sizeof ((X)->elements) > 1 && \ |
(array) ((X)->elements[1]->elements)) |
#define ASN1_GET_ATTR_NAME(X) ((X)->elements[0]->value) |
#define ASN1_GET_ATTR_VALUES(X) ((X)->elements[1]->elements->value) |
|
#define ASN1_RESULTCODE(X) (int)((X)->elements[1]->elements[0]->value->cast_to_int()) |
#define ASN1_RESULTSTRING(X) ((X)->elements[1]->elements[2]->value) |
#define ASN1_RESULTREFS(X) ((X)->elements[1]->elements[3]->elements) |
|
|
|
|
|
inherit .protocol; |
|
private { |
string bound_dn; |
string md5_password; |
string ldap_basedn; |
int ldap_scope; |
int ldap_deref; |
int ldap_sizelimit; |
int ldap_timelimit; |
mapping lauth = ([]); |
object default_filter_obj; |
result last_rv; |
|
|
} |
|
|
protected function(string:string) get_attr_decoder (string attr, |
DO_IF_DEBUG (void|int nowarn)) |
{ |
if (mapping(string:mixed) attr_descr = get_attr_type_descr (attr)) { |
if (function(string:string) decoder = |
syntax_decode_fns[attr_descr->syntax_oid]) |
return decoder; |
#ifdef LDAP_DEBUG |
else if (!get_constant_name (attr_descr->syntax_oid)) |
werror ("Warning: Unknown syntax %O for attribute %O - " |
"binary content assumed.\n", attr_descr->syntax_oid, attr); |
#endif |
} |
#ifdef LDAP_DEBUG |
else if (!nowarn && !has_suffix (attr, ";binary") && !has_value (attr, ";binary;")) |
werror ("Warning: Couldn't fetch attribute description for %O - " |
"binary content assumed.\n", attr); |
#endif |
return 0; |
} |
|
|
protected function(string:string) get_attr_encoder (string attr) |
{ |
if (mapping(string:mixed) attr_descr = get_attr_type_descr (attr)) { |
if (function(string:string) encoder = |
syntax_encode_fns[attr_descr->syntax_oid]) |
return encoder; |
#ifdef LDAP_DEBUG |
else if (!get_constant_name (attr_descr->syntax_oid)) |
werror ("Warning: Unknown syntax %O for attribute %O - " |
"binary content assumed.\n", attr_descr->syntax_oid, attr); |
#endif |
} |
#ifdef LDAP_DEBUG |
else if (!has_suffix (attr, ";binary") && !has_value (attr, ";binary;")) |
werror ("Warning: Couldn't fetch attribute description for %O - " |
"binary content assumed.\n", attr); |
#endif |
return 0; |
} |
|
typedef string|Charset.DecodeError| |
array(string|Charset.DecodeError) ResultAttributeValue; |
|
typedef mapping(string:ResultAttributeValue) ResultEntry; |
|
|
|
|
|
|
class result |
{ |
|
private int resultcode = LDAP_SUCCESS; |
private string resultstring; |
private int actnum = 0; |
private array(ResultEntry) entry = ({}); |
private int flags; |
array(string) referrals; |
|
|
|
private int first_undecoded_entry = 0; |
|
array(ResultEntry) decode_entries (array(object) entries) |
{ |
array(ResultEntry) res = ({}); |
|
#define DECODE_ENTRIES(SET_DN, SET_ATTR) do { \ |
if (flags & SEARCH_MULTIVAL_ARRAYS_ONLY) { \ |
foreach (entries, object entry) { \ |
object derent = entry->elements[1]; \ |
if (array(object) derattribs = ASN1_GET_ATTR_ARRAY (derent)) { \ |
string dn; \ |
{SET_DN;} \ |
ResultEntry attrs = (["dn": dn]); \ |
foreach (derattribs, object derattr) { \ |
string attr; \ |
{SET_ATTR;} \ |
sscanf (attr, "%[^;]", string bare_attr); \ |
if (mapping(string:mixed) attr_descr = \ |
get_attr_type_descr (bare_attr)) { \ |
if (attr_descr["SINGLE-VALUE"]) { \ |
if (sizeof (attrs[attr])) { \ |
DO_IF_DEBUG ( \ |
if (sizeof (attrs[attr]) > 1) \ |
ERROR ("Got multple values %O for single valued " \ |
"attribute %O.\n", attrs[attr], attr); \ |
); \ |
attrs[attr] = attrs[attr][0]; \ |
} \ |
else \ |
attrs[attr] = 0; \ |
} \ |
} \ |
DO_IF_DEBUG ( \ |
else if (dn != "") \ |
werror ("Warning: Couldn't fetch attribute description for %O - " \ |
"multivalued attribute assumed.\n", attr); \ |
); \ |
} \ |
res += ({attrs}); \ |
} \ |
} \ |
} \ |
\ |
else { \ |
foreach (entries, object entry) { \ |
object derent = entry->elements[1]; \ |
if (array(object) derattribs = ASN1_GET_ATTR_ARRAY (derent)) { \ |
string dn; \ |
{SET_DN;} \ |
ResultEntry attrs = (["dn": ({dn})]); \ |
foreach (derattribs, object derattr) { \ |
string attr; \ |
{SET_ATTR;} \ |
} \ |
res += ({attrs}); \ |
} \ |
} \ |
} \ |
} while (0) |
|
if (flags & SEARCH_LOWER_ATTRS) |
DECODE_ENTRIES (dn = ASN1_GET_DN (derent), { |
attrs[attr = lower_case (ASN1_GET_ATTR_NAME (derattr))] = |
ASN1_GET_ATTR_VALUES (derattr); |
}); |
else |
DECODE_ENTRIES (dn = ASN1_GET_DN (derent), { |
attrs[attr = ASN1_GET_ATTR_NAME (derattr)] = |
ASN1_GET_ATTR_VALUES (derattr); |
}); |
|
#undef DECODE_ENTRIES |
|
return res; |
} |
|
protected void decode_entry (ResultEntry ent) |
{ |
|
|
|
|
|
#define DECODE_DN(DN) do { \ |
if (mixed err = catch (DN = utf8_to_string (DN))) { \ |
string errmsg = describe_error (err) + \ |
"The string is the DN of an entry.\n"; \ |
if (flags & SEARCH_RETURN_DECODE_ERRORS) \ |
DN = Charset.DecodeError (DN, -1, 0, errmsg); \ |
else \ |
throw (Charset.DecodeError (DN, -1, 0, errmsg)); \ |
} \ |
} while (0) |
|
ResultAttributeValue dn = m_delete (ent, "dn"); |
if (stringp (dn)) |
DECODE_DN (dn); |
else |
DECODE_DN (dn[0]); |
|
#ifdef LDAP_DECODE_DEBUG |
|
#define DECODE_VALUE(ATTR, VALUE, DECODER) do { \ |
if (mixed err = catch {VALUE = DECODER (VALUE);}) { \ |
mapping descr1, descr2; \ |
catch (descr1 = get_attr_type_descr (ATTR)); \ |
catch (descr2 = get_attr_type_descr (ATTR, 1)); \ |
string errmsg = \ |
sprintf ("%s" \ |
"The string occurred in the value of attribute %O " \ |
"in entry with DN %O.\n" \ |
"Used decoder %O for attribute type %O, " \ |
"server reports %O.\n", \ |
describe_error (err), ATTR, stringp (dn) ? dn : dn[0], \ |
decoder, descr1, descr2); \ |
if (flags & SEARCH_RETURN_DECODE_ERRORS) \ |
VALUE = Charset.DecodeError (VALUE, -1, 0, errmsg); \ |
else \ |
throw (Charset.DecodeError (VALUE, -1, 0, errmsg)); \ |
} \ |
} while (0) |
|
#else |
|
#define DECODE_VALUE(ATTR, VALUE, DECODER) do { \ |
if (mixed err = catch {VALUE = DECODER (VALUE);}) { \ |
string errmsg = \ |
sprintf ("%s" \ |
"The string occurred in the value of attribute %O " \ |
"in entry with DN %O.\n", \ |
describe_error (err), ATTR, stringp (dn) ? dn : dn[0]); \ |
if (flags & SEARCH_RETURN_DECODE_ERRORS) \ |
VALUE = Charset.DecodeError (VALUE, -1, 0, errmsg); \ |
else \ |
throw (Charset.DecodeError (VALUE, -1, 0, errmsg)); \ |
} \ |
} while (0) |
|
#endif |
|
foreach (ent; string attr; ResultAttributeValue vals) { |
if (function(string:string) decoder = |
get_attr_decoder (attr, DO_IF_DEBUG (stringp (dn) ? |
dn == "" : dn[0] == ""))) { |
if (stringp (vals)) { |
DECODE_VALUE (attr, vals, decoder); |
ent[attr] = vals; |
} |
else |
foreach (vals; int i; string|Charset.DecodeError val) { |
DECODE_VALUE (attr, val, decoder); |
vals[i] = val; |
} |
} |
} |
|
#undef DECODE_VALUE |
|
ent->dn = dn; |
} |
|
|
|
|
object|int create(array(object) entries, int stuff, int flags) { |
|
|
|
this::flags = flags; |
|
|
|
|
|
|
if (!sizeof (entries)) { |
seterr (LDAP_LOCAL_ERROR); |
THROW(({"LDAP: Internal error.\n",backtrace()})); |
return -ldap_errno; |
} |
DWRITE("result.create: %O\n", entries[-1]); |
|
|
resultcode = ASN1_RESULTCODE (entries[-1]); |
DWRITE("result.create: code=%d\n", resultcode); |
resultstring = ASN1_RESULTSTRING (entries[-1]); |
if (resultstring == "") |
resultstring = 0; |
else if (ldap_version >= 3) |
if (mixed err = catch (resultstring = utf8_to_string (resultstring))) |
DWRITE ("Failed to decode result string %O: %s", |
resultstring, describe_error (err)); |
DWRITE("result.create: str=%O\n", resultstring); |
#ifdef V3_REFERRALS |
|
if(resultcode == 10) { |
referrals = ({}); |
foreach(ASN1_RESULTREFS (entries[-1]), object ref1) |
referrals += ({ ref1->value }); |
DWRITE("result.create: refs=%O\n", referrals); |
} |
#endif |
DWRITE("result.create: elements=%d\n", sizeof(entries)); |
|
entry = decode_entries (entries[..<1]); |
|
#if 0 |
|
switch(stuff) { |
case 1: |
DWRITE("result.create: stuff=1\n"); |
break; |
default: |
DWRITE("result.create: stuff=%d\n", stuff); |
break; |
} |
#endif |
|
return this; |
} |
|
|
|
|
|
|
int error_number() { return resultcode; } |
|
|
|
|
|
|
|
|
|
string error_string() { |
return resultstring || ldap_error_strings[resultcode]; |
} |
|
|
|
|
|
|
string server_error_string() {return resultstring;} |
|
|
|
|
|
|
int num_entries() { return sizeof (entry); } |
|
|
|
|
|
|
|
int count_entries() { return sizeof (entry) - actnum; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResultEntry fetch(int|void idx) |
{ |
if (!undefinedp (idx)) actnum = idx; |
if (actnum >= num_entries() || actnum < 0) return 0; |
|
if (ldap_version < 3) |
return entry[actnum]; |
|
ResultEntry ent = entry[actnum]; |
|
if (actnum == first_undecoded_entry) { |
decode_entry (ent); |
first_undecoded_entry++; |
} |
else if (actnum > first_undecoded_entry) { |
ent = copy_value (ent); |
decode_entry (ent); |
} |
|
return ent; |
} |
|
|
|
|
|
|
|
|
|
string|Charset.DecodeError get_dn() |
{ |
string|array(string) dn = fetch()->dn; |
return stringp (dn) ? dn : dn[0]; |
} |
|
|
|
|
|
|
|
void first() { actnum = 0; } |
|
|
|
|
|
|
|
|
int next() { |
if (actnum < sizeof (entry)) |
actnum++; |
return count_entries(); |
} |
|
array(ResultEntry) fetch_all() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
for (; first_undecoded_entry < sizeof (entry); first_undecoded_entry++) |
decode_entry (entry[first_undecoded_entry]); |
return entry; |
} |
|
} |
|
|
|
|
|
#define SIMPLE_RESULT(STR, STUFF, FLAGS) \ |
result (({.ldap_privates.ldap_der_decode (STR)}), (STUFF), (FLAGS)) |
|
#ifdef ENABLE_PAGED_SEARCH |
#define IF_ELSE_PAGED_SEARCH(X,Y) X |
#else /* !ENABLE_PAGED_SEARCH */ |
#define IF_ELSE_PAGED_SEARCH(X,Y) Y |
#endif |
|
#ifdef LDAP_PROTOCOL_PROFILE |
#define PROFILE(STR, CODE...) DWRITE_PROF(STR + ": %O\n", gauge {CODE;}) |
#else |
#define PROFILE(STR, CODE...) do { CODE; } while(0) |
#endif |
|
private int chk_ver() { |
|
if ((ldap_version != 2) && (ldap_version != 3)) { |
seterr (LDAP_PROTOCOL_ERROR); |
THROW(({"LDAP: Unknown/unsupported protocol version.\n",backtrace()})); |
return -ldap_errno; |
} |
return 0; |
} |
|
private int chk_binded() { |
|
|
switch (ldap_version) { |
case 2: |
if (!bound_dn) { |
seterr (LDAP_PROTOCOL_ERROR); |
THROW(({"LDAP: Must bind first.\n",backtrace()})); |
return -ldap_errno; |
} |
break; |
case 3: |
if (!bound_dn) |
bind(); |
break; |
} |
return 0; |
} |
|
private int chk_dn(string dn) { |
|
if ((!dn) || (!sizeof(dn))) { |
seterr (LDAP_INVALID_DN_SYNTAX); |
THROW(({"LDAP: Invalid DN syntax.\n",backtrace()})); |
return -ldap_errno; |
} |
return 0; |
} |
|
|
mapping info; |
|
#ifndef PARSE_RFCS |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create(string|mapping(string:mixed)|void url, object|void context) |
{ |
|
info = ([ "code_revision" : |
sprintf("%d.%d.%d",(int)__REAL_VERSION__,__REAL_MINOR__,__REAL_BUILD__) |
]); |
|
if(!url || !sizeof(url)) |
url = LDAP_DEFAULT_URL; |
|
if (mappingp (url)) |
lauth = url; |
else |
lauth = parse_ldap_url(url); |
|
if(!stringp(lauth->scheme) || |
((lauth->scheme != "ldap") |
#if constant(SSL.Cipher) |
&& (lauth->scheme != "ldaps") |
#endif |
)) { |
THROW(({"Unknown scheme in server URL.\n",backtrace()})); |
} |
|
if(!lauth->host) |
lauth += ([ "host" : LDAP_DEFAULT_HOST ]); |
if(!lauth->port) |
lauth += ([ "port" : lauth->scheme == "ldap" ? LDAP_DEFAULT_PORT : LDAPS_DEFAULT_PORT ]); |
|
#if constant(SSL.Cipher) |
if(lauth->scheme == "ldaps" && !context) { |
context = SSL.Context(); |
} |
#endif |
|
Stdio.File low_fd = Stdio.File(); |
|
if(!(low_fd->connect(lauth->host, lauth->port))) { |
|
seterr (LDAP_SERVER_DOWN, strerror (low_fd->errno())); |
|
|
|
|
|
ERROR ("Failed to connect to LDAP server: %s\n", ldap_rem_errstr); |
} |
|
#if constant(SSL.Cipher) |
if(lauth->scheme == "ldaps") { |
SSL.File ssl_fd = SSL.File(low_fd, context); |
if (!ssl_fd->connect()) { |
ERROR("Failed to connect to LDAPS server.\n"); |
} |
::create(ssl_fd); |
info->tls_version = ldapfd->version; |
} else |
::create(low_fd); |
#else |
if(lauth->scheme == "ldaps") { |
THROW(({"LDAP: LDAPS is not available without SSL support.\n",backtrace()})); |
} |
else |
::create(low_fd); |
#endif |
|
DWRITE("client.create: connected!\n"); |
|
DWRITE("client.create: remote = %s\n", low_fd->query_address()); |
DWRITE_HI("client.OPEN: " + lauth->host + ":" + (string)(lauth->port) + " - OK\n"); |
|
reset_options(); |
} |
#endif |
|
void reset_options() |
|
|
|
{ |
set_scope (lauth->scope || SCOPE_BASE); |
set_basedn (lauth->basedn); |
ldap_deref = 0; |
ldap_sizelimit = 0; |
ldap_timelimit = 0; |
last_rv = 0; |
} |
|
private mixed send_bind_op(string name, string password) { |
|
|
object msgval, vers, namedn, auth; |
string pass = password; |
password = "censored"; |
|
vers = Integer(ldap_version); |
namedn = OctetString(name); |
auth = ASN1_CONTEXT_OCTET_STRING(0, pass); |
|
|
msgval = ASN1_APPLICATION_SEQUENCE(0, ({vers, namedn, auth})); |
|
return do_op(msgval); |
} |
|
private mixed send_starttls_op(object|void context) { |
|
object msgval; |
#if constant(SSL.Cipher) |
|
|
if(ldapfd->context) |
{ |
THROW(({"LDAP: TLS/SSL already established.\n",backtrace()})); |
} |
|
|
|
|
|
msgval = ASN1_APPLICATION_SEQUENCE(23, ({OctetString("1.3.6.1.4.1.1466.20037")})); |
|
do_op(msgval); |
int result = ASN1_RESULTCODE(.ldap_privates.ldap_der_decode (readbuf)); |
if(result!=0) return 0; |
|
if(!context) |
{ |
context = SSL.Context(); |
} |
object _f = ldapfd; |
ldapfd = SSL.File(_f, context); |
return ldapfd->connect(); |
#endif |
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
int start_tls (void|SSL.Context context) { |
#if constant(SSL.Cipher) |
if(ldap_version < 3) |
{ |
seterr (LDAP_PROTOCOL_ERROR); |
THROW(({"LDAP: Unknown/unsupported protocol version.\n",backtrace()})); |
return -ldap_errno; |
} |
|
return send_starttls_op(context||UNDEFINED); |
|
return 1; |
#else |
return 0; |
#endif |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int bind (string|void dn, string|void password, int|void version) { |
|
mixed raw; |
string pass = password; |
password = "censored"; |
|
if (!version) |
version = LDAP_DEFAULT_VERSION; |
if (chk_ver()) |
return 0; |
|
if (bound_dn && ldap_version <= 2) { |
ERROR ("Can't bind a connection more than once in LDAPv2.\n"); |
return 0; |
} |
|
if (!stringp(dn)) |
dn = mappingp(lauth->ext) ? lauth->ext->bindname||"" : ""; |
if (!stringp(pass)) |
pass = ""; |
ldap_version = version; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
pass = string_to_utf8(pass); |
} |
if(intp(raw = send_bind_op(dn, pass))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
bound_dn = md5_password = 0; |
last_rv = SIMPLE_RESULT (raw, 1, 0); |
if (!last_rv->error_number()) { |
bound_dn = dn; |
md5_password = Crypto.MD5.hash (pass); |
} |
DWRITE_HI("client.BIND: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return !!bound_dn; |
|
} |
|
|
private int send_unbind_op() { |
|
|
writemsg(ASN1_APPLICATION_OCTET_STRING(2, "")); |
|
|
|
return 1; |
} |
|
#if 0 |
void destroy() { |
|
|
|
|
|
} |
#endif |
|
|
|
int unbind () { |
|
if (send_unbind_op() < 1) { |
THROW(({error_string()+"\n",backtrace()})); |
return -ldap_errno; |
} |
bound_dn = md5_password = 0; |
DWRITE_HI("client.UNBIND: OK\n"); |
|
} |
|
private int|string send_op_withdn(int op, string dn) { |
|
|
return do_op(ASN1_APPLICATION_OCTET_STRING(op, dn)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
int delete (string dn) { |
|
mixed raw; |
|
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if (chk_dn(dn)) |
return 0; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
} |
if(intp(raw = send_op_withdn(10, dn))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
last_rv = SIMPLE_RESULT (raw, 0, 0); |
DWRITE_HI("client.DELETE: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return !last_rv->error_number(); |
|
} |
|
private int|string send_compare_op(string dn, string attr, string value) { |
|
|
object msgval; |
|
msgval = ASN1_APPLICATION_SEQUENCE(14, |
({ OctetString(dn), |
Sequence( ({ OctetString(attr), OctetString(value) }) ) |
}) |
); |
|
return do_op(msgval); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int compare (string dn, string attr, string value) { |
|
mixed raw; |
|
|
|
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if (chk_dn(dn)) |
return 0; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
if (function(string:string) encoder = get_attr_encoder (attr)) |
value = encoder (value); |
} |
if(intp(raw = send_compare_op(dn, attr, value))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
last_rv = SIMPLE_RESULT (raw, 0, 0); |
DWRITE_HI("client.COMPARE: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return last_rv->error_number() == LDAP_COMPARE_TRUE; |
|
} |
|
private int|string send_add_op(string dn, mapping(string:array(string)) attrs) { |
|
|
object msgval; |
string atype; |
array(object) oatt = ({}); |
|
foreach(indices(attrs), atype) { |
string aval; |
array(object) ohlp = ({}); |
|
foreach(values(attrs[atype]), aval) |
ohlp += ({OctetString(aval)}); |
oatt += ({Sequence( |
({OctetString(atype), |
Set(ohlp) |
})) |
}); |
} |
|
msgval = ASN1_APPLICATION_SEQUENCE(8, |
({ OctetString(dn), Sequence(oatt) })); |
|
return do_op(msgval); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int add (string dn, mapping(string:array(string)) attrs) { |
|
mixed raw; |
|
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if (chk_dn(dn)) |
return 0; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
attrs += ([]); |
|
|
foreach (indices(attrs), string attr) |
if (function(string:string) encoder = get_attr_encoder (attr)) |
attrs[attr] = map (attrs[attr], encoder); |
} |
if(intp(raw = send_add_op(dn, attrs))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
last_rv = SIMPLE_RESULT (raw, 0, 0); |
DWRITE_HI("client.ADD: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return !last_rv->error_number(); |
|
} |
|
protected mapping(string:array(string)) simple_read (string object_name, |
object filter, |
array attrs) |
|
|
|
{ |
object|int search_request = |
make_search_op(object_name, 0, 0, 0, 0, 0, filter, attrs); |
|
|
string|int raw; |
if(intp(raw = do_op(search_request))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
mapping(string:array(string)) res = ([]); |
|
do { |
object response = .ldap_privates.ldap_der_decode(raw); |
if (ASN1_GET_RESULTAPP (response) == 5) break; |
|
response = response->elements[1]; |
foreach(ASN1_GET_ATTR_ARRAY (response), object attr) |
res[lower_case (ASN1_GET_ATTR_NAME (attr))] = ASN1_GET_ATTR_VALUES (attr); |
|
|
if (intp(raw = readmsg(0))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
} while (1); |
|
return res; |
} |
|
protected mapping(string:array(string)) root_dse; |
|
array(string) get_root_dse_attr (string attr) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
attr = lower_case (attr); |
|
if (!root_dse || !has_index (root_dse, attr)) { |
PROFILE("get_root_dse_attr", { |
|
multiset(string) attrs = root_dse ? (<>) : |
|
(< |
|
|
|
"createtimestamp", |
"modifytimestamp", |
"creatorsname", |
"modifiersname", |
"subschemasubentry", |
"attributetypes", |
"objectclasses", |
"matchingrules", |
"matchingruleuse", |
|
|
"namingcontexts", |
"altserver", |
"supportedextension", |
"supportedcontrol", |
"supportedsaslmechanisms", |
"supportedldapversion", |
>); |
attrs[attr] = 1; |
|
mapping(string:array(string)) res = |
simple_read ("", get_cached_filter ("(objectClass=*)"), indices (attrs)); |
|
foreach (indices (res), string attr) |
|
|
|
|
if (function(string:string) decoder = |
get_attr_decoder (attr, DO_IF_DEBUG (1))) |
res[attr] = map (res[attr], decoder); |
|
if (root_dse) |
root_dse[attr] = res[attr]; |
else { |
root_dse = res; |
if (!root_dse[attr]) root_dse[attr] = 0; |
} |
}); |
} |
|
return root_dse[attr]; |
} |
|
protected object make_control (string control_type, void|string value, |
void|int critical) |
{ |
array(object) seq = ({OctetString (control_type), |
Boolean (critical)}); |
if (value) seq += ({OctetString (value)}); |
return Sequence (seq); |
} |
|
protected multiset(string) supported_controls; |
|
multiset(string) get_supported_controls() |
|
|
|
|
|
|
{ |
if (!supported_controls) { |
if (array(string) res = get_root_dse_attr ("supportedControl")) |
supported_controls = mkmultiset (res); |
else |
supported_controls = (<>); |
} |
return supported_controls; |
} |
|
object make_filter (string filter) |
|
|
|
|
|
|
|
|
|
{ |
return Protocols.LDAP.make_filter (filter, ldap_version); |
} |
|
object get_cached_filter (string filter) |
|
|
|
|
|
|
|
|
{ |
return Protocols.LDAP.get_cached_filter (filter, ldap_version); |
} |
|
object get_default_filter() |
|
|
|
|
|
|
|
{ |
if (!default_filter_obj && lauth->filter) |
default_filter_obj = make_filter (lauth->filter); |
return default_filter_obj; |
} |
|
private object|int make_search_op(string basedn, int scope, int deref, |
int sizelimit, int timelimit, |
int attrsonly, object filter, |
void|array(string) attrs) |
{ |
|
|
|
array(object) ohlp; |
|
ohlp = ({filter}); |
if (arrayp(attrs)) { |
array(object) o2 = ({}); |
foreach(attrs, string s2) |
o2 += ({OctetString(s2)}); |
ohlp += ({Sequence(o2)}); |
} else |
ohlp += ({Sequence(({}))}); |
|
return ASN1_APPLICATION_SEQUENCE(3, |
({ OctetString(basedn), |
Enumerated(scope), |
Enumerated(deref), |
Integer(sizelimit), |
Integer(timelimit), |
Boolean(attrsonly ? -1 : 0), |
@ohlp |
})) ; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result|int search (string|object|void filter, array(string)|void attrs, |
int|void attrsonly, |
void|mapping(string:array(int|string)) controls, |
void|int flags) { |
|
int id; |
object entry; |
array(object) entries = ({}); |
|
DWRITE_HI("client.SEARCH: %O\n", filter); |
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
|
if (!objectp (filter)) |
if (mixed err = catch { |
if (!filter) |
filter = get_default_filter(); |
else |
filter = make_filter (filter); |
}) { |
if (objectp (err) && err->is_ldap_filter_error) { |
seterr (LDAP_FILTER_ERROR); |
return 0; |
} |
else |
throw (err); |
} |
|
object|int search_request = |
make_search_op(ldap_basedn, ldap_scope, ldap_deref, |
ldap_sizelimit, ldap_timelimit, attrsonly, filter, |
attrs||lauth->attributes); |
|
if(intp(search_request)) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
array(object) common_controls; |
if (controls) { |
common_controls = allocate (sizeof (controls)); |
int i; |
foreach (controls; string type; array(int|string) data) |
common_controls[i++] = |
make_control (type, [string] data[1], [int] data[0]); |
} |
else common_controls = ({}); |
|
#if 0 |
|
|
|
if (get_supported_controls()[LDAP_SERVER_DOMAIN_SCOPE_OID]) { |
|
|
common_controls += ({make_control (LDAP_SERVER_DOMAIN_SCOPE_OID)}); |
} |
#endif |
|
#ifdef ENABLE_PAGED_SEARCH |
get_supported_controls(); |
#endif |
|
object cookie = OctetString(""); |
do { |
PROFILE("send_search_op", { |
array ctrls = common_controls; |
IF_ELSE_PAGED_SEARCH ( |
if (supported_controls[LDAP_PAGED_RESULT_OID_STRING]) { |
|
|
ctrls += ({make_control ( |
LDAP_PAGED_RESULT_OID_STRING, |
Sequence( |
({ |
|
Integer(0x7fffffff), |
cookie, |
}))->get_der(), |
sizeof(cookie->value))}); |
},); |
object controls; |
if (sizeof(ctrls)) { |
controls = ASN1_CONTEXT_SEQUENCE(0, ctrls); |
} |
|
string|int raw; |
if(intp(raw = do_op(search_request, controls))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
entry = .ldap_privates.ldap_der_decode (raw); |
}); |
|
PROFILE("entries++", { |
entries += ({entry}); |
while (ASN1_GET_RESULTAPP(entry) != 5) { |
string|int raw; |
PROFILE("readmsg", raw = readmsg(id)); |
if (intp(raw)) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
entry = .ldap_privates.ldap_der_decode (raw); |
entries += ({entry}); |
} |
}); |
|
|
cookie = 0; |
IF_ELSE_PAGED_SEARCH({ |
if ((ASN1_RESULTCODE(entry) != 10) && |
(sizeof(entry->elements) > 2)) { |
object controls = entry->elements[2]; |
foreach(controls->elements, object control) { |
if (!control->constructed || |
!sizeof(control) || |
control->elements[0]->type_name != "OCTET STRING") { |
|
|
continue; |
} |
if (control->elements[0]->value != |
LDAP_PAGED_RESULT_OID_STRING) { |
|
|
continue; |
} |
if (sizeof(control) == 1) continue; |
int pos = 1; |
if (control->elements[1]->type_name == "BOOLEAN") { |
if (sizeof(control) == 2) continue; |
pos = 2; |
} |
if (control->elements[pos]->type_name != "OCTET STRING") { |
|
continue; |
} |
object control_info = |
.ldap_privates.ldap_der_decode(control->elements[pos]->value); |
if (!control_info->constructed || |
sizeof(control_info) < 2 || |
control_info->elements[1]->type_name != "OCTET STRING") { |
|
continue; |
} |
if (sizeof(control_info->elements[1]->value)) { |
cookie = control_info->elements[1]; |
} |
} |
if (cookie) { |
|
entries = entries[..<1]; |
} |
} |
|
},); |
} while (cookie); |
|
PROFILE("result", last_rv = result (entries, 0, flags)); |
if(objectp(last_rv)) |
seterr (last_rv->error_number(), last_rv->error_string()); |
|
|
|
DWRITE_HI("client.SEARCH: %s (entries: %d)\n", |
last_rv->error_string(), last_rv->num_entries()); |
return last_rv; |
|
} |
|
mapping(string:string|array(string)) read ( |
string object_name, |
void|string filter, |
void|array(string) attrs, |
void|int attrsonly, |
void|mapping(string:array(int|string)) controls, |
void|int flags) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if(ldap_version == 3) { |
object_name = string_to_utf8 (object_name); |
if (filter) filter = string_to_utf8(filter); |
} |
|
object|int search_request = |
make_search_op (object_name, 0, ldap_deref, |
ldap_sizelimit, ldap_timelimit, attrsonly, |
filter || get_cached_filter ("(objectClass=*)"), |
attrs); |
|
if(intp(search_request)) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
object ctrls; |
if (controls) { |
array(object) control_list = allocate (sizeof (controls)); |
int i; |
foreach (controls; string type; array(int|string) data) |
control_list[i++] = |
make_control (type, [string] data[1], [int] data[0]); |
if (sizeof (control_list)) |
ctrls = ASN1_CONTEXT_SEQUENCE(0, control_list); |
} |
|
object entry; |
PROFILE ("send_get_op", { |
string|int raw; |
if(intp(raw = do_op(search_request, ctrls))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
entry = .ldap_privates.ldap_der_decode (raw); |
}); |
|
array(object) entries; |
PROFILE("entries++", { |
entries = ({entry}); |
while (ASN1_GET_RESULTAPP(entry) != 5) { |
string|int raw; |
|
|
PROFILE("readmsg", raw = readmsg(0)); |
if (intp(raw)) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
entry = .ldap_privates.ldap_der_decode (raw); |
entries += ({entry}); |
} |
}); |
|
PROFILE ("result", last_rv = result (entries, 0, flags)); |
seterr (last_rv->error_number(), last_rv->error_string()); |
|
if (ldap_errno != LDAP_SUCCESS) return 0; |
return last_rv->fetch(); |
} |
|
string|array(string) read_attr (string object_name, |
string attr, |
void|string filter, |
void|mapping(string:array(int|string)) controls) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
if (mapping(string:string|array(string)) res = |
read (object_name, filter, ({attr}), 0, controls, |
SEARCH_MULTIVAL_ARRAYS_ONLY)) { |
m_delete (res, "dn"); |
|
|
return get_iterator (res)->value(); |
} |
return 0; |
} |
|
|
int get_protocol_version() {return ldap_version;} |
|
|
|
|
|
|
|
|
string set_basedn (string base_dn) { |
|
string old_dn = ldap_basedn; |
|
if(ldap_version == 3) { |
base_dn = string_to_utf8(base_dn); |
} |
ldap_basedn = base_dn; |
DWRITE_HI("client.SET_BASEDN = " + base_dn + "\n"); |
return old_dn; |
} |
|
|
|
string get_basedn() {return utf8_to_string (ldap_basedn);} |
|
|
|
|
|
string get_bound_dn() {return bound_dn;} |
|
|
|
|
|
string get_bind_password_hash() {return md5_password;} |
|
|
|
|
|
|
|
|
|
|
int set_scope (int|string scope) { |
|
int old_scope = ldap_scope; |
|
|
if(stringp(scope)) |
switch (lower_case(scope)) { |
case "sub": scope = SCOPE_SUB; break; |
case "one": scope = SCOPE_ONE; break; |
case "base": scope = SCOPE_BASE; break; |
default: ERROR ("Invalid scope %O.\n", scope); |
} |
else |
if (!(<SCOPE_BASE, SCOPE_ONE, SCOPE_SUB>)[scope]) |
ERROR ("Invalid scope %O.\n", scope); |
|
ldap_scope = scope; |
DWRITE_HI("client.SET_SCOPE = %O\n", scope); |
return old_scope; |
} |
|
|
|
string get_scope() |
{return ([SCOPE_BASE: "base", SCOPE_ONE: "one", SCOPE_SUB: "sub"])[ldap_scope];} |
|
|
|
|
|
int set_option (int opttype, int value) { |
|
DWRITE_HI("client.SET_OPTION: %O = %O\n", opttype, value); |
switch (opttype) { |
case 1: |
|
ldap_deref = value; |
|
|
break; |
case 2: |
|
ldap_sizelimit = value; |
|
|
break; |
case 3: |
|
ldap_timelimit = value; |
|
|
break; |
case 4: |
default: return -1; |
} |
|
|
return 0; |
} |
|
|
|
int get_option (int opttype) { |
|
|
DWRITE_HI("client.GET_OPTION: %O\n", opttype); |
switch (opttype) { |
case 1: |
return ldap_deref; |
case 2: |
return ldap_sizelimit; |
case 3: |
return ldap_timelimit; |
case 4: |
} |
|
return -1; |
} |
|
mapping(string:mixed) get_parsed_url() {return lauth;} |
|
|
|
|
|
private int|string send_modify_op(string dn, |
mapping(string:array(mixed)) attropval) { |
|
|
object o, msgval; |
string atype; |
array(object) oatt = ({}), attrarr; |
|
|
foreach(indices(attropval), atype) { |
if(!intp((attropval[atype])[0])) |
return seterr(LDAP_PROTOCOL_ERROR); |
attrarr = ({}); |
for(int ix=1; ix<sizeof(attropval[atype]); ix++) |
attrarr += ({ OctetString(attropval[atype][ix]) }); |
|
o = Sequence( ({OctetString(atype), Set(attrarr) })); |
|
|
|
oatt += ({ Sequence( ({Enumerated((attropval[atype])[0]), o})) }); |
} |
|
msgval = ASN1_APPLICATION_SEQUENCE(6, |
({ OctetString(dn), Sequence(oatt) })); |
|
return do_op(msgval); |
} |
|
private int|string send_modifydn_op(string dn, string newrdn, |
int deleteoldrdn, string newsuperior) { |
|
object msgval; |
array seq=({ |
OctetString(dn), |
OctetString(newrdn), |
Boolean(deleteoldrdn) |
}); |
if(newsuperior) |
seq+=({ OctetString(newsuperior)}); |
|
msgval = ASN1_APPLICATION_SEQUENCE(12, seq); |
|
return do_op(msgval); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int modifydn (string dn, string newrdn, int deleteoldrdn, |
string|void newsuperior) { |
|
mixed raw; |
|
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if (chk_dn(dn)) |
return 0; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
newrdn = string_to_utf8(newrdn); |
if(newsuperior) newsuperior = string_to_utf8(newsuperior); |
} |
if(intp(raw = send_modifydn_op(dn, newrdn, deleteoldrdn, newsuperior))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
last_rv = SIMPLE_RESULT (raw, 0, 0); |
DWRITE_HI("client.MODIFYDN: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return !last_rv->error_number(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int modify (string dn, mapping(string:array(int(0..2)|string)) attropval) { |
|
mixed raw; |
|
if (chk_ver()) |
return 0; |
if (chk_binded()) |
return 0; |
if (chk_dn(dn)) |
return 0; |
if(ldap_version == 3) { |
dn = string_to_utf8(dn); |
attropval += ([]); |
|
|
foreach (indices (attropval), string attr) |
if (function(string:string) encoder = get_attr_encoder (attr)) { |
array(int(0..2)|string) op = attropval[attr] + ({}); |
for (int i = sizeof (op); --i;) |
op[i] = encoder (op[i]); |
attropval[attr] = op; |
} |
} |
if(intp(raw = send_modify_op(dn, attropval))) { |
THROW(({error_string()+"\n",backtrace()})); |
return 0; |
} |
|
last_rv = SIMPLE_RESULT (raw, 0, 0); |
DWRITE_HI("client.MODIFY: %s\n", last_rv->error_string()); |
seterr (last_rv->error_number(), last_rv->error_string()); |
return !last_rv->error_number(); |
|
} |
|
|
|
|
|
array|int get_referrals() { |
if(last_rv->referrals) |
return last_rv->referrals; |
return 0; |
} |
|
|
mapping(string:mixed) parse_url (string ldapuri) |
{ |
return parse_ldap_url (ldapuri); |
} |
|
|
|
|
protected mapping(string:array(string)) query_subschema (string dn, |
array(string) attrs) |
|
|
|
|
|
|
|
|
|
{ |
mapping(string:array(string)) subschema_response; |
int utf8_decode_dns; |
|
if (dn == "" && root_dse) |
subschema_response = root_dse; |
else { |
subschema_response = |
simple_read (dn, get_cached_filter ("(objectClass=*)"), ({"subschemaSubentry"})); |
utf8_decode_dns = 1; |
} |
|
if (subschema_response) |
if (array(string) subschema_dns = subschema_response->subschemasubentry) { |
if (sizeof (subschema_dns) == 1) |
return simple_read ( |
utf8_decode_dns ? utf8_to_string (subschema_dns[0]) : subschema_dns[0], |
get_cached_filter ("(objectClass=subschema)"), attrs); |
|
else { |
|
|
|
|
|
mapping(string:array(string)) res = ([]); |
foreach (subschema_dns, string subschema_dn) { |
if (mapping(string:array(string)) subres = simple_read ( |
utf8_decode_dns ? utf8_to_string (subschema_dn) : subschema_dn, |
get_cached_filter ("(objectClass=subschema)"), attrs)) |
foreach (indices (subres), string attr) |
res[attr] += subres[attr]; |
} |
return res; |
} |
} |
|
return 0; |
} |
|
protected mapping(string:mixed) parse_schema_terms ( |
string str, |
mapping(string:string|multiset|mapping) known_terms, |
string errmsg_prefix) |
|
|
|
|
{ |
string orig_str = str, oid; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!sscanf (str, "(%*[ ]%[^ '()]%*[ ]%s", oid, str)) |
ERROR ("%sExpected '(' at beginning: %O\n", |
errmsg_prefix, orig_str); |
if (!sizeof (oid)) |
ERROR ("%sObject identifier missing at start: %O\n", |
errmsg_prefix, orig_str); |
|
mapping(string:mixed) res = (["oid": oid]); |
|
while (!sscanf (str, ")%s", str)) { |
int pos = sizeof (str); |
|
|
|
|
sscanf (str, "%[^ '()]%*[ ]%s", string term_id, str); |
if (!sizeof (term_id)) |
ERROR ("%sTerm identifier expected at pos %d: %O\n", |
errmsg_prefix, sizeof (orig_str) - pos, orig_str); |
|
string|multiset(string) term_syntax = known_terms[term_id] || known_terms[""]; |
switch (term_syntax) { |
case 0: |
ERROR ("%sUnknown term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
|
case "flag": |
res[term_id] = 1; |
break; |
|
case "oid": |
string parse_oid() |
{ |
sscanf (str, "%[^ '()]%*[ ]%s", string oid, str); |
if (!sizeof (oid)) |
ERROR ("%sExpected oid after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
return oid; |
}; |
res[term_id] = parse_oid(); |
break; |
|
case "oids": |
if (sscanf (str, "(%*[ ]%s", str)) { |
array(string) list = ({}); |
while (1) { |
if (str == "") |
ERROR ("%sUnterminated parenthesis after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
sscanf (str, "%[^ '()$]%*[ ]%s", string oid, str); |
if (!sizeof (oid)) |
ERROR ("%sExpected oid after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
list += ({oid}); |
if (sscanf (str, ")%*[ ]%s", str)) |
break; |
if (!sscanf (str, "$%*[ ]%s", str)) |
ERROR ("%sExpected '$' between oids after term %O at pos %d: " |
"%O\n", errmsg_prefix, term_id, sizeof (orig_str) - pos, |
orig_str); |
} |
res[term_id] = list; |
} |
else |
res[term_id] = ({parse_oid()}); |
break; |
|
case "oidlen": { |
|
|
|
|
int ms_kludge; |
string oid; |
if (has_prefix (str, "'")) { |
ms_kludge = 1; |
sscanf (str, "'%[^'{]%s", oid, str); |
} |
else { |
sscanf (str, "%[^ '(){]%s", oid, str); |
} |
|
if (!sizeof (oid)) |
ERROR ("%sExpected numeric oid after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
term_id = lower_case (term_id); |
res[term_id + "_oid"] = oid; |
if (sscanf (str, "{%d}%s", int len, str)) |
res[term_id + "_len"] = len; |
if (ms_kludge) { |
if (!sscanf (str, "'%*[ ]%s", str)) |
ERROR ("%sUnterminated quoted oid after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
} |
else |
sscanf (str, "%*[ ]%s", str); |
break; |
} |
|
case "qdstring": |
string parse_qdstring (string what) |
{ |
string qstr; |
switch (sscanf (str, "'%[^']'%*[ ]%s", qstr, str)) { |
case 0: |
ERROR ("%sExpected %s after term %O at pos %d: %O\n", |
errmsg_prefix, what, term_id, sizeof (orig_str) - pos, orig_str); |
case 1: |
ERROR ("%sUnterminated %s after term %O at pos %d: %O\n", |
errmsg_prefix, what, term_id, sizeof (orig_str) - pos, orig_str); |
} |
if (catch (qstr = utf8_to_string (qstr))) |
ERROR ("%sMalformed UTF-8 in %s after term %O at pos %d: %O\n", |
errmsg_prefix, what, term_id, sizeof (orig_str) - pos, orig_str); |
return ldap_decode_string (qstr); |
}; |
res[term_id] = parse_qdstring ("quoted string"); |
break; |
|
case "qdstrings": |
if (sscanf (str, "(%*[ ]%s", str)) { |
array(string) list = ({}); |
do { |
if (str == "") |
ERROR ("%sUnterminated parenthesis after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
list += ({parse_qdstring ("quoted string in list")}); |
} while (!sscanf (str, ")%*[ ]%s", str)); |
res[term_id] = list; |
} |
else |
res[term_id] = ({parse_qdstring ("quoted string")}); |
break; |
|
case "qdescr": |
string parse_qdescr (string what) |
{ |
string name; |
|
|
|
switch (sscanf (str, "'%[^']'%*[ ]%s", name, str)) { |
case 0: |
ERROR ("%sExpected %s after term %O at pos %d: %O\n", |
errmsg_prefix, what, term_id, sizeof (orig_str) - pos, orig_str); |
case 1: |
ERROR ("%sInvalid chars in %s after term %O at pos %d: %O\n", |
errmsg_prefix, what, term_id, sizeof (orig_str) - pos, orig_str); |
} |
return name; |
}; |
res[term_id] = parse_qdescr ("quoted descr"); |
break; |
|
case "qdescrs": |
if (sscanf (str, "(%*[ ]%s", str)) { |
array(string) list = ({}); |
do { |
if (str == "") |
ERROR ("%sUnterminated parenthesis after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
list += ({parse_qdescr ("quoted descr in list")}); |
} while (!sscanf (str, ")%*[ ]%s", str)); |
res[term_id] = list; |
} |
else |
res[term_id] = ({parse_qdescr ("quoted descr")}); |
break; |
|
default: |
if (multisetp (term_syntax) || mappingp (term_syntax)) { |
|
sscanf (str, "%[^ '()]%*[ ]%s", string choice, str); |
if (!sizeof (choice)) |
ERROR ("%sExpected keyword after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
if (term_syntax[1]) choice = lower_case (choice); |
string|int lookup = term_syntax[choice]; |
if (!lookup) |
ERROR ("%sUnknown keyword after term %O at pos %d: %O\n", |
errmsg_prefix, term_id, sizeof (orig_str) - pos, orig_str); |
res[term_id] = stringp (lookup) ? lookup : choice; |
break; |
} |
|
ERROR ("Unknown syntax %O in known_perms.\n", term_syntax); |
} |
} |
|
if (str != "") |
ERROR ("%sUnexpected data after ending ')' at pos %d: %O\n", |
errmsg_prefix, sizeof (orig_str) - sizeof (str) - 1, orig_str); |
|
return res; |
} |
|
protected constant attr_type_term_syntax = ([ |
"NAME": "qdescrs", |
"DESC": "qdstring", |
"OBSOLETE": "flag", |
"SUP": "oid", |
"EQUALITY": "oid", |
"ORDERING": "oid", |
"SUBSTR": "oid", |
"SYNTAX": "oidlen", |
"SINGLE-VALUE": "flag", |
"COLLECTIVE": "flag", |
"NO-USER-MODIFICATION": "flag", |
"USAGE": ([ |
1: 1, |
|
|
|
"userapplications": "userApplications", |
"directoryoperation": "directoryOperation", |
"distributedoperation": "distributedOperation", |
"dsaoperation": "dSAOperation" |
]), |
"": "qdstrings" |
]); |
|
protected mapping(string:mapping(string:mixed)) attr_type_descrs; |
|
mapping(string:mixed) get_attr_type_descr (string attr, void|int standard_attrs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
if (!(<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'>)[attr[0]]) |
attr = lower_case (attr); |
|
if (mapping(string:mixed) descr = standard_attrs != 1 && |
_standard_attr_type_descrs[attr]) |
return descr; |
if (standard_attrs == 2) |
return 0; |
|
if (!attr_type_descrs) { |
attr_type_descrs = ([]); |
if (mapping(string:array(string)) subschema = |
query_subschema (ldap_basedn, ({"attributeTypes"}))) |
if (array(string) attr_types = subschema->attributetypes) { |
|
|
|
array(mapping(string:mixed)) incomplete = ({}); |
|
foreach (attr_types, string attr_type) { |
mapping(string:mixed) descr = parse_schema_terms ( |
utf8_to_string (attr_type), |
attr_type_term_syntax, |
"Error in attributeTypes when querying schema: "); |
if (descr->SUP) incomplete += ({descr}); |
attr_type_descrs[descr->oid] = descr; |
foreach (descr->NAME, string name) |
attr_type_descrs[lower_case (name)] = descr; |
} |
|
void complete (mapping(string:mixed) descr) { |
string sup = lower_case (descr->SUP); |
mapping(string:mixed) sup_descr = |
attr_type_descrs[sup] || |
(standard_attrs != 1 && _standard_attr_type_descrs[sup]); |
if (!sup_descr) |
ERROR ("Inconsistency in schema: " |
"Got SUP reference to unknown attribute: %O\n", descr); |
if (sup_descr->SUP) |
complete (sup_descr); |
foreach (indices (sup_descr), string term) |
if (!has_index (descr, term)) |
descr[term] = sup_descr[term]; |
}; |
foreach (incomplete, mapping(string:mixed) descr) |
complete (descr); |
} |
} |
|
return attr_type_descrs[attr]; |
} |
|
#ifdef PARSE_RFCS |
|
int main (int argc, array(string) argv) |
{ |
|
|
|
array(array(string)) sections = ({}); |
|
{ |
|
array(string) cont; |
while (string line = Stdio.stdin->gets()) { |
if (sscanf (line, "%[0-9.]%*[ \t]%s", string sno, string shdr) == 3 && |
sno != "" && shdr != "") { |
if (cont) sections += ({cont}); |
if (has_suffix (sno, ".")) sno = sno[..<1]; |
cont = ({sno, shdr}); |
} |
else if (cont) |
cont += ({line}); |
} |
if (cont) sections += ({cont}); |
} |
|
foreach (sections, array(string) cont) |
for (int n = 0; n < sizeof (cont); n++) { |
if (sscanf (cont[n], "%*[ \t](%*s") == 2 && |
(n == 0 || String.trim_whites (cont[n-1]) == "")) { |
string expr = String.trim_whites (cont[n]), s; |
for (n++; n < sizeof (cont) && (s = String.trim_whites (cont[n])) != ""; n++) |
expr += " " + s; |
|
mapping descr; |
if (mixed err = |
catch (descr = parse_schema_terms ( |
expr, attr_type_term_syntax, ""))) |
werror (describe_error (err)); |
|
write ("constant ATD_%s = ([ // %s, %s\n", |
replace (cont[1], " ", "_"), argv[1], cont[0]); |
foreach (({"oid", "NAME", "DESC", "OBSOLETE", "SUP", "EQUALITY", "ORDERING", |
"SUBSTR", "syntax_oid", "syntax_len", "SINGLE-VALUE", "COLLECTIVE", |
"NO-USER-MODIFICATION", "USAGE"}), string term) { |
if (mixed val = descr[term]) { |
if (arrayp (val)) |
write (" %O: ({%s}),\n", term, |
map (val, lambda (string s) {return sprintf ("%O", s);}) * ", "); |
else { |
if (string sym = (<"oid", "syntax_oid">)[term] && |
get_constant_name (val)) |
write (" %O: %s,\n", term, sym); |
else |
write (" %O: %O,\n", term, val); |
} |
} |
} |
write ("]);\n"); |
} |
} |
} |
|
#endif |
|
|