aa370a1999-04-24Johan Schön // LDAP client protocol implementation for Pike. //
e96ce22000-10-11Honza Petrous // $Id: client.pike,v 1.16 2000/10/11 11:55:36 hop Exp $
aa370a1999-04-24Johan Schön // // Honza Petrous, hop@unibase.cz // // ---------------------------------------------------------------------- // // History: // // v0.0 1998-05-25 Starting up! // v1.0 1998-06-21 Core functions (open, bind, unbind, delete, add, // compare, search), only V2 operations, // some additional restrictions // v1.1u 1998-07-03 Unfinished version (for testing) // v1.2a 1998-07-14 New beta code (I love jump over versions like Roxen ;) // - added asn1_enumerated, asn1_boolean, asn1_nulllist // and asn1_application_null // - search op: corrected ASN1 type // - unbind op: corrected ASN1 type // v1.2d 1998-08 - added logging of bad LDAP PDU in readmsg // v1.3 1998-09-18 - resolved "null attribute list" bug in search // (Now is compatible with MS Exchange ;-) // v1.9 1999-03 - redesign of LDAP.pmod // !!! Library uses new ASN1 API => uncompatible !!! // !!! with Pike 0.5 (needed Pike 0.6 or higher) !!! // - sliced library code to the several files: // * LDAP.pmod/protocol.pike // (low level code) // * LDAP.pmod/client.pike // (main code) // * LDAP.pmod/ldap_errors.h // (error array) // * LDAP.pmod/ldap_globals.h // (global defininitions) // * LDAP.pmod/ldap_privates.pmod // (ASN.1 LDAP-related classes and hacks) // - changed default of 'ldap_scope' to 0 // - search filter now correctly processed '\(' & '\)' // [! Still unimplemented escaped conditions chars!] // // v1.10 1999-03-28 - moved core to the new 'protocol' code // 1999-03-28 - rewritten ldap_[op] startup code // // v1.11 1999-04-10 - search filter now processed multiple wild '*' chars // [ Escaping untested, yet ]
b68dde2000-02-12Honza Petrous // v1.13 2000-02-12 - fixed search NOT op bug (end revision normalized)
aa370a1999-04-24Johan Schön //
4ad4cd2000-02-17Honza Petrous // v1.14 2000-02-17 - added decoding of UTF8 strings for v3 protocol //
7bddfb2000-09-14Honza Petrous // newer versions - see CVS at roxen.com (hop) // // - corrected deUTF8 values in result // - //
aa370a1999-04-24Johan Schön // Specifications: // // RFC 1558 (search filter representations) // RFC 1777,1778,1779 (version2 spec) // RFC 1823 (v2 API) // RFC 2251,2252,2253,2254,2255,2256 (version3 spec) // draft-ietf-asid-ldap-c-api-00.txt (v3 API) // RFC2279 (UTF-8) // // Interesting, applicable // RFC 2307 (LDAP as network information services; draft?)
92dbd31999-08-24Fredrik Hübinette (Hubbe) #if constant(.ldap_privates.ldap_der_decode)
aa370a1999-04-24Johan Schön  #include "ldap_globals.h" #include "ldap_errors.h" // ------------------------ // ASN.1 decode macros #define ASN1_DECODE_RESULTAPP(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->get_tag()) #define ASN1_DECODE_RESULTCODE(X) (int)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[0]->value->cast_to_int()) #define ASN1_DECODE_RESULTSTRING(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->elements[1]->value) #define ASN1_DECODE_ENTRIES(X) _New_decode(X) #define ASN1_DECODE_DN(X) (string)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[0]->value) #define ASN1_DECODE_RAWDEBUG(X) (.ldap_privates.ldap_der_decode(X)->debug_string())
eeae561999-08-27Honza Petrous #define ASN1_GET_ATTR_ARRAY(X) (array)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[1]->elements)
aa370a1999-04-24Johan Schön #define ASN1_GET_ATTR_NAME(X) ((X)->elements[0]->value) inherit .protocol; private int binded = 0; // flag for v2 operations private string ldap_basedn = ""; // baseDN private int ldap_scope = 0; // 0: base, 1: onelevel, 2: subtree private int ldap_deref = 0; // 0: ... private int ldap_sizelimit = 0; private int ldap_timelimit = 0;
7bddfb2000-09-14Honza Petrous  private mapping lauth = ([]);
aa370a1999-04-24Johan Schön  class result // ------------------ { private int resultcode = LDAP_SUCCESS; //private string resultstring = LDAP_SUCCESS_STR; private int entrycnt = 0; private int actnum = 0; private array(mapping(string:array(string))) entry = ({}); int error_number() { return(resultcode); } string error_string() { return(ldap_errlist[resultcode]); } int num_entries() { return(entrycnt); } int count_entries() { return(entrycnt - actnum); }
7bddfb2000-09-14Honza Petrous  private array _get_attr_values(int ver, object x) {
aa370a1999-04-24Johan Schön  array res = ({}); if(!sizeof(x->elements)) return(res); foreach(x->elements[1]->elements, object val1) res += ({ val1->value });
7bddfb2000-09-14Honza Petrous  if(ver == 3) { // deUTF8 res = Array.map(res, utf8_to_string); }
aa370a1999-04-24Johan Schön  return(res); } private array _New_decode(array ar) { array res = ({}); array entry1; mapping attrs; foreach(ar, string raw1) { attrs = (["dn":({ASN1_DECODE_DN(raw1)})]); entry1 = ASN1_GET_ATTR_ARRAY(raw1); foreach(entry1, object attr1) {
7bddfb2000-09-14Honza Petrous  attrs += ([ASN1_GET_ATTR_NAME(attr1):_get_attr_values(ldap_version, attr1)]);
aa370a1999-04-24Johan Schön  } res += ({attrs}); }
4ad4cd2000-02-17Honza Petrous 
aa370a1999-04-24Johan Schön  return (res); } // _New_decode object|int create(array rawres, int|void stuff) { // rawres: array of result in raw format, but WITHOUT LDAP PDU !!! // stuff: 1=bind result; ... int lastel = sizeof(rawres) - 1; if (lastel < 0) { ::seterr (LDAP_LOCAL_ERROR); THROW(({"LDAP: Internal error.\n",backtrace()})); return(-::ldap_errno); } //DWRITE(sprintf("DEB: lastel=%d\n",lastel)); DWRITE(sprintf("result.create: rawres=%O\n",rawres[lastel])); // The last element of 'rawres' is result itself resultcode = ASN1_DECODE_RESULTCODE(rawres[lastel]); DWRITE(sprintf("result.create: code=%d\n",resultcode)); #if 0 resultstring = ASN1_DECODE_RESULTSTRING(rawres[lastel]); DWRITE(sprintf("result.create: resstr=%s\n",resultstring)); #endif // referral (v3 mode) // ... DWRITE(sprintf("result.create: elements=%d\n",lastel+1)); if (lastel) { // Have we any entry? entry = ASN1_DECODE_ENTRIES(rawres[..lastel-1]); entrycnt = sizeof(entry); //num_entries(); } #if 0 // Context specific proccessing of 'rawres' switch(stuff) { case 1: DWRITE("result.create: stuff=1\n"); break; default: DWRITE(sprintf("result.create: stuff=%d\n", stuff)); } #endif return(this_object()); } // create int|mapping(string:array(string)) fetch(int|void ix) { if (!ix) ix = actnum + 1; if ((ix <= num_entries()) && (ix > 0)) { actnum = ix - 1; return(entry[actnum]); } return(0); } string get_dn() { return(fetch()["dn"][0]); } void first() { actnum = 0; } int next() { if (actnum < (num_entries()-1)) { actnum++; return(count_entries()); } return(0); } } // end of class 'result' --------------- // helper functions 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() { // For version 2: we must be 'binded' first !!! if ((ldap_version == 2) && !binded) { seterr (LDAP_PROTOCOL_ERROR); THROW(({"LDAP: Must binded first.\n",backtrace()})); return(-ldap_errno); }
7bddfb2000-09-14Honza Petrous  if ((ldap_version == 3) && !binded) bind();
aa370a1999-04-24Johan Schön  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); } // API function (ldap_open) // // create(string|void server) //
7bddfb2000-09-14Honza Petrous  // server: server URL in form "ldap://hostname/basedn???!bindname=
aa370a1999-04-24Johan Schön  void create(string|void server) { if(!server || !sizeof(server))
7bddfb2000-09-14Honza Petrous  server = LDAP_DEFAULT_URL; lauth = parse_url(server); if(!stringp(lauth->scheme) || (lauth->scheme != "ldap")) { THROW(({"Unknown scheme in server URL.\n",backtrace()})); } if(!lauth->host) lauth += ([ "host" : parse_url(LDAP_DEFAULT_URL)->host ]); if(!lauth->port) lauth += ([ "port" : parse_url(LDAP_DEFAULT_URL)->port ]);
aa370a1999-04-24Johan Schön 
7bddfb2000-09-14Honza Petrous  ::create(lauth->host, lauth->port); if(!::connected) {
aa370a1999-04-24Johan Schön  THROW(({"Failed to connect to LDAP server.\n",backtrace()})); } DWRITE(sprintf("client.create: remote = %s\n", query_address()));
7bddfb2000-09-14Honza Petrous  DWRITE_HI("client.OPEN: " + lauth->host + ":" + (string)(lauth->port) + " - OK\n");
aa370a1999-04-24Johan Schön  binded = 0;
7bddfb2000-09-14Honza Petrous  if(lauth->scope) set_scope(lauth->scope); if(lauth->basedn) set_basedn(lauth->basedn);
aa370a1999-04-24Johan Schön  } // create private mixed send_bind_op(string name, string password) { // Simple BIND operation object msgval, vers, namedn, auth, app; vers = Standards.ASN1.Types.asn1_integer(ldap_version); namedn = Standards.ASN1.Types.asn1_octet_string(name); auth = ASN1_CONTEXT_OCTET_STRING(0, password); // SASL credentials ommited msgval = ASN1_APPLICATION_SEQUENCE(0, ({vers, namedn, auth})); return (do_op(msgval)); } // API function (ldap_bind) // // bind(string|void name, string|void password, int|void proto) // // name: // password: // proto: protocol version, supported 2 and 3 int bind (string|void name, string|void password, int|void proto) { int id; mixed raw; object rv; if (!proto) proto = LDAP_DEFAULT_VERSION; if (chk_ver()) return(-ldap_errno); if (!stringp(name))
7bddfb2000-09-14Honza Petrous  name = mappingp(lauth->ext) ? lauth->ext->bindname||"" : "";
aa370a1999-04-24Johan Schön  if (!stringp(password)) password = ""; ldap_version = proto; if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  name = string_to_utf8(name); password = string_to_utf8(password);
aa370a1999-04-24Johan Schön  } if(intp(raw = send_bind_op(name, password))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw}),1); if (!rv->error_number()) binded = 1; DWRITE_HI(sprintf("client.BIND: %s\n", rv->error_string())); return (seterr (rv->error_number())); } // bind private int send_unbind_op() { // UNBIND operation writemsg(ASN1_APPLICATION_OCTET_STRING(2, "")); //ldap::close(); return (1); } void destroy() { //send_unbind_op(); destruct(this_object()); } // API function (ldap_unbind) // // unbind() // int unbind () { if (send_unbind_op() < 1) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } binded = 0; DWRITE_HI("client.UNBIND: OK\n"); } // unbind private int|string send_op_withdn(int op, string dn) { // DELETE, ... return (do_op(ASN1_APPLICATION_OCTET_STRING(op, dn))); } // API function (ldap_delete) // // delete(string dn) // // dn: DN of deleted object int delete (string dn) { int id; mixed raw; object rv; if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if (chk_dn(dn)) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn);
aa370a1999-04-24Johan Schön  } if(intp(raw = send_op_withdn(10, dn))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw})); DWRITE_HI(sprintf("client.DELETE: %s\n", rv->error_string())); return (seterr (rv->error_number())); } // delete private int|string send_compare_op(string dn, array(string) aval) { // COMPARE object msgval; msgval = ASN1_APPLICATION_SEQUENCE(14, ({ Standards.ASN1.Types.asn1_octet_string(dn), Standards.ASN1.Types.asn1_sequence( ({ Standards.ASN1.Types.asn1_octet_string(aval[0]), Standards.ASN1.Types.asn1_octet_string(aval[1]) })) }) ); return (do_op(msgval)); } // API function (ldap_compare) // // compare(string dn, array(string) aval) // // dn: DN of compared object // aval: attribute value int compare (string dn, array(string) aval) { int id; mixed raw; object rv; // if (!aval || sizeof(aval)<2) // error if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if (chk_dn(dn)) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); aval = Array.map(aval, string_to_utf8);
aa370a1999-04-24Johan Schön  } if(intp(raw = send_compare_op(dn, aval))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw})); DWRITE_HI(sprintf("client.COMPARE: %s\n", rv->error_string())); return (seterr (rv->error_number())); } // compare private int|string send_add_op(string dn, mapping(string:array(string)) attrs) { // ADD object msgval; string atype; array(object) oatt = ({}); foreach(indices(attrs), atype) { string aval; array(object) ohlp = ({}); foreach(values(attrs[atype]), aval) ohlp += ({Standards.ASN1.Types.asn1_octet_string(aval)}); oatt += ({Standards.ASN1.Types.asn1_sequence( ({Standards.ASN1.Types.asn1_octet_string(atype), Standards.ASN1.Types.asn1_set(ohlp) })) }); } msgval = ASN1_APPLICATION_SEQUENCE(8, ({Standards.ASN1.Types.asn1_octet_string(dn), Standards.ASN1.Types.asn1_sequence(oatt) })); return (do_op(msgval)); } // API function (ldap_add) // // add(string dn, mapping(string:array(string)) attrs) // // dn: DN of compared object // ttrs: attribute(s) and value int add (string dn, mapping(string:array(string)) attrs) { int id; mixed raw; object rv; if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if (chk_dn(dn)) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); array(string) keys = indices(attrs); array(array(string)) vals = values(attrs); attrs = mkmapping(Array.map(keys, string_to_utf8), Array.map(vals, Array.map, string_to_utf8));
aa370a1999-04-24Johan Schön  } if(intp(raw = send_add_op(dn, attrs))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw})); DWRITE_HI(sprintf("client.ADD: %s\n", rv->error_string())); return (seterr (rv->error_number())); } // add private static array(string) filter_get_sub1expr(string fstr) { // returns one-level brackets enclosed expressions array(string) rvarr = ({}); int leftflg = 0, nskip = 0; for(int ix=0; ix<sizeof(fstr); ix++) if((fstr[ix] == '(') && (!ix || (fstr[ix-1] != '\\'))) { leftflg = ix+1; while(++ix < sizeof(fstr)) { if((fstr[ix] == '(') && (fstr[ix-1] != '\\')) { nskip++; // deeper expr. continue; } if((fstr[ix] == ')') && (fstr[ix-1] != '\\')) if(nskip) { // we are deeply? nskip--; continue; } else { // ok, here is end of expr. rvarr += ({fstr[leftflg..(ix-1)]}); leftflg = 0; break; } } // while } if(leftflg) { ; // silent error: Missed right-enclosed bracket ! } //DWRITE(sprintf("client.sub1: arr=%O\n",rvarr)); return(rvarr); } /*private*/ object make_simple_filter(string filter) { // filter expression parser - only simple expressions! object rv; int op, ix; DWRITE(sprintf("client.make_simple_filter: filter: [%s]\n", filter)); if((op = predef::search(filter, ">=")) > 0) { // greater or equal DWRITE("client.make_simple_filter: [>=]\n"); return(ASN1_CONTEXT_SEQUENCE(5, ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), Standards.ASN1.Types.asn1_octet_string(filter[(op+2)..]) }))); } if((op = predef::search(filter, "<=")) > 0) { // less or equal DWRITE("client.make_simple_filter: [<=]\n"); return(ASN1_CONTEXT_SEQUENCE(6, ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), Standards.ASN1.Types.asn1_octet_string(filter[(op+2)..]) }))); } if((op = predef::search(filter, "=")) > 0) { // equal, substring if((filter[-2] == '=') && (filter[-1] == '*')) // any (=*) return(make_simple_filter(filter[..op-1])); ix = predef::search(filter[(op+1)..], "*"); if ((ix != -1) && (filter[op+ix] != '\\')) { // substring object ohlp; array oarr = ({}), ahlp = ({}); array filtval = filter[(op+1)..] / "*"; // escape processing for(int cnt = 0; cnt < sizeof(filtval); cnt++) { if(cnt) { if(sizeof(filtval[cnt-1]) && filtval[cnt-1][-1] == '\\') ahlp[-1] = reverse(reverse(ahlp[-1])[1..]) + filtval[cnt]; else ahlp += ({ filtval[cnt] }); } else ahlp = ({ filtval[cnt] }); } // for // filter elements processing (left, center & right) ix = sizeof(ahlp); for (int cnt = 0; cnt < ix; cnt++) if(!cnt) { // leftmost element if(sizeof(ahlp[0])) oarr = ({ASN1_CONTEXT_OCTET_STRING(0, ahlp[0])}); } else if(cnt == ix-1) { // rightmost element if(sizeof(ahlp[ix-1])) oarr += ({ASN1_CONTEXT_OCTET_STRING(2, ahlp[ix-1])}); } else { // inside element if(sizeof(ahlp[cnt])) oarr += ({ASN1_CONTEXT_OCTET_STRING(1, ahlp[cnt])}); } // for DWRITE(sprintf("client.make_simple_filter: substring: [%s]:\n%O\n", filter, ahlp)); return(ASN1_CONTEXT_SEQUENCE(4, ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), Standards.ASN1.Types.asn1_sequence(oarr) }))); } else { // equal DWRITE("client.make_simple_filter: [=]\n"); return(ASN1_CONTEXT_SEQUENCE(3, ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), Standards.ASN1.Types.asn1_octet_string(filter[(op+1)..]) }))); } } // if equal,substring // present DWRITE("client.make_simple_filter: [present]\n"); return(ASN1_CONTEXT_OCTET_STRING(7, filter)); } /*private*/ object|int make_filter(string filter) { // filter expression parser object ohlp; array(object) oarr = ({}); int op ; DWRITE("client.make_filter: filter=["+filter+"]\n"); // strip leading and trailing spaces while(filter[0] == ' ') filter = filter[1..]; while(filter[-1] == ' ') filter = reverse(reverse(filter)[1..]); // strip leading and trailing brackets #if 1 if(filter[0] == '(') { int ix; string f2 = reverse(filter[1..]); if((ix = predef::search(f2, ")")) > -1) { filter = reverse(f2[ix+1..]); //DWRITE("DEB: make_filter: filter=["+filter+"]\n"); return(make_filter(filter)); } return(-1); // error in filter expr. } #endif op = -1; DWRITE(sprintf("client.make_filter: ftype=%c\n",filter[0])); switch (filter[0]) { case '&': // and case '|': // or foreach(filter_get_sub1expr(filter[1..]), string sub1expr) if (objectp(ohlp = make_filter(sub1expr))) oarr += ({ohlp}); else return(0); // error: Filter parameter error! DWRITE(sprintf("client.make_filter: expr_cnt=%d\n",sizeof(oarr))); //ohlp = Standards.ASN1.Encode.asn1_set(@oarr); op = 0; if (filter[0] == '|') op = 1; return(ASN1_CONTEXT_SET(op, oarr)); case '!': // not if (objectp(ohlp = make_filter(filter_get_sub1expr(filter[1..])[0])))
b68dde2000-02-12Honza Petrous  return(ASN1_CONTEXT_SEQUENCE(2, ({ ohlp}) ));
aa370a1999-04-24Johan Schön  else return(0); // error: Filter parameter error! break; default : // we assume simple filter return(make_simple_filter(filter)); } } private int|string send_search_op(string basedn, int scope, int deref, int sizelimit, int timelimit, int attrsonly, string filter, void|array(string) attrs){ // SEARCH // limitations: !!! sizelimit and timelimit should be unsigned int !!! object msgval, ofilt; array(object) ohlp; if(!objectp(ofilt = make_filter(filter))) { return(-seterr(LDAP_FILTER_ERROR)); } ohlp = ({ofilt}); if (arrayp(attrs)) { //explicitly defined attributes array(object) o2 = ({}); //DWRITE(sprintf("DEB: attrs=%O\n",attrs)); foreach(attrs, string s2) o2 += ({Standards.ASN1.Types.asn1_octet_string(s2)}); ohlp += ({Standards.ASN1.Types.asn1_sequence(o2)}); } else ohlp += ({Standards.ASN1.Types.asn1_sequence(({}))}); msgval = ASN1_APPLICATION_SEQUENCE(3, ({ Standards.ASN1.Types.asn1_octet_string(basedn), ASN1_ENUMERATED(scope), ASN1_ENUMERATED(deref), Standards.ASN1.Types.asn1_integer(sizelimit), Standards.ASN1.Types.asn1_integer(timelimit), ASN1_BOOLEAN(attrsonly ? -1 : 0), @ohlp })) ; return (do_op(msgval)); } // API function (ldap_search) // // search(string filter, int|void attrsonly, array(string)|void attrs) // // filter: search filter // attrsonly: flag // attrsy: attribute(s) name
e96ce22000-10-11Honza Petrous  object|int search (string|void filter, int|void attrsonly, array(string)|void attrs) {
aa370a1999-04-24Johan Schön  int id; mixed raw; array(string) rawarr = ({}); mixed rv;
7bddfb2000-09-14Honza Petrous  filter=filter||lauth->filter; // default from LDAP URI
aa370a1999-04-24Johan Schön  DWRITE_HI("client.SEARCH: " + (string)filter + "\n"); if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  filter = string_to_utf8(filter);
aa370a1999-04-24Johan Schön  } if(intp(raw = send_search_op(ldap_basedn, ldap_scope, ldap_deref, ldap_sizelimit, ldap_timelimit, attrsonly, filter, attrs))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rawarr = ({raw}); while (ASN1_DECODE_RESULTAPP(raw) != 5) { raw = readmsg(id); if (intp(raw)) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } if((ASN1_DECODE_RESULTAPP(raw) == 4) || (ASN1_DECODE_RESULTAPP(raw) == 5)) rawarr += ({raw}); else { DWRITE(sprintf("client.search: APP[%d]\n",ASN1_DECODE_RESULTAPP(raw))); // in version 3 - referrals } } // while rv = result(rawarr); if(objectp(rv)) seterr (rv->error_number()); //if (rv->error_number() || !rv->num_entries()) // if error or entries=0 // rv = rv->error_number(); DWRITE_HI(sprintf("client.SEARCH: %s (entries: %d)\n", rv->error_string(), rv->num_entries())); return(rv); } // search // API function (ldap_setbasedn) // // set_basedn(string base_dn) // // base_dn: base DN for search string set_basedn (string base_dn) { string old_dn = ldap_basedn; if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  base_dn = string_to_utf8(base_dn);
aa370a1999-04-24Johan Schön  } ldap_basedn = base_dn; DWRITE_HI("client.SET_BASEDN = " + base_dn + "\n"); return(old_dn); } // API function (ldap_setscope) // // set_scope(string base_dn) // // scope: scope; 0: base, 1: onelevel, 2: subtree int set_scope (int scope) { int old_scope = ldap_scope; ldap_scope = scope; DWRITE_HI("client.SET_SCOPE = " + (string)scope + "\n"); return(old_scope); } // API function (ldap_setoption) // // set_option(int option_type, mixed value) // // option_type: LDAP_OPT_xxx // value: new value for option int set_option (int opttype, int value) { DWRITE_HI("client.SET_OPTION: " + (string)opttype + " = " + (string)value + "\n"); switch (opttype) { case 1: // LDAP_OPT_DEREF //if (intp(value)) ldap_deref = value; //else // return(-1); break; case 2: // LDAP_OPT_SIZELIMIT //if (intp(value)) ldap_sizelimit = value; //else // return(-1); break; case 3: // LDAP_OPT_TIMELIMIT //if (intp(value)) ldap_timelimit = value; //else // return(-1); break; case 4: // LDAP_OPT_REFERRALS default: return(-1); } return(0); } // API function (ldap_getoption) // // get_option(int option_type) // // option_type: LDAP_OPT_xxx int get_option (int opttype) { DWRITE_HI("client.GET_OPTION: " + (string)opttype + "\n"); switch (opttype) { case 1: // LDAP_OPT_DEREF return(ldap_deref); case 2: // LDAP_OPT_SIZELIMIT return(ldap_sizelimit); case 3: // LDAP_OPT_TIMELIMIT return(ldap_timelimit); case 4: // LDAP_OPT_REFERRALS } return(-1); } private int|string send_modify_op(string dn, mapping(string:array(mixed)) attropval) { // MODIFY 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 += ({Standards.ASN1.Types.asn1_octet_string( (attropval[atype])[ix])}); // if(sizeof(attrarr)) // attributevalue ? o = Standards.ASN1.Types.asn1_sequence( ({Standards.ASN1.Types.asn1_octet_string(atype), Standards.ASN1.Types.asn1_set(attrarr) })); // else // o = Standards.ASN1.Encode.asn1_sequence( // Standards.ASN1.Encode.asn1_octet_string(atype)); oatt += ({Standards.ASN1.Types.asn1_sequence( ({ASN1_ENUMERATED((attropval[atype])[0]), o }))}); } //foreach msgval = ASN1_APPLICATION_SEQUENCE(6, ({ Standards.ASN1.Types.asn1_octet_string(dn), Standards.ASN1.Types.asn1_sequence(oatt) })); return (do_op(msgval)); } // API function (ldap_modify) // // modify(string dn, mapping(string:array(mix)) attropval) // // dn: DN of compared object // attropval: attribute(s), operation and value(s) int modify (string dn, mapping(string:array(mixed)) attropval) { int id; mixed raw; object rv; if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if (chk_dn(dn)) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); array(string) keys = indices(attropval); array(array(mixed)) vals = values(attropval); attropval = mkmapping(Array.map(keys, string_to_utf8), Array.map(vals, Array.map, lambda(mixed x) { return (stringp(x)? string_to_utf8(x) : x); }));
aa370a1999-04-24Johan Schön  } if(intp(raw = send_modify_op(dn, attropval))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw})); DWRITE_HI(sprintf("client.MODIFY: %s\n", rv->error_string())); return (seterr (rv->error_number())); } // modify
7bddfb2000-09-14Honza Petrous  // API function // // parse_url(string ldapuri) // // ldapuri: LDAP URL mapping|int parse_url (string ldapuri) { // ldap://machine.at.home.cz:123/c=cz?attr1,attr2,attr3?sub?(uid=*)?!bindname=uid=hop,dc=unibase,dc=cz" string url=ldapuri, s, scheme; array ar; mapping res; s = (url / ":")[0]; url = url[sizeof(s)..]; res = ([ "scheme" : s ]); #ifdef LDAP_URL_STRICT if (url[..2] != "://") return(-1); #endif s = (url[3..] / "/")[0]; url = url[sizeof(s)+4..]; res += ([ "host" : (s / ":")[0] ]); if(sizeof(s / ":") > 1) res += ([ "port" : (int)((s / ":")[1]) ]); ar = url / "?"; switch (sizeof(ar)) { case 5: if (sizeof(ar[4])) { mapping extensions = ([]); foreach(ar[4] / ",", string ext) { int ix = predef::search(ext, "="); if(ix) extensions += ([ ext[..(ix-1)] : replace(ext[ix+1..],QUOTED_COMMA, ",") ]); } if (sizeof(extensions)) res += ([ "ext" : extensions ]); } //case 5: res += ([ "ext" : ar[4] ]); case 4: res += ([ "filter" : ar[3] ]); case 3: switch (ar[2]) { case "sub": res += ([ "scope" : 2 ]); break; case "one": res += ([ "scope" : 1 ]); break; default: res += ([ "scope" : 0]); // = "base" } case 2: res += sizeof(ar[1]) ? ([ "attributes" : ar[1] / "," ]) : ([]); case 1: res += ([ "basedn" : ar[0] ]); } return (res); } //parse_uri
92dbd31999-08-24Fredrik Hübinette (Hubbe) #endif