pike.git / lib / modules / Protocols.pmod / LDAP.pmod / client.pike

version» Context lines:

pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1:   #pike __REAL_VERSION__      // LDAP client protocol implementation for Pike.   // - // $Id: client.pike,v 1.34 2004/06/18 14:54:03 anders Exp $ + // $Id: client.pike,v 1.35 2004/10/13 15:45:28 wellhard Exp $   //   // 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,
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:55:   // - corrected deUTF8 values in result   // -   //   // 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) + // RFC 2279 (UTF-8) + // RFC 2696 (paged requests)   //   // Interesting, applicable   // RFC 2307 (LDAP as network information services; draft?)         #if constant(.ldap_privates.ldap_der_decode)      #include "ldap_globals.h"      #include "ldap_errors.h"    -  + #if constant(SSL.sslfile)   import SSL.constants; -  - #ifdef LDAP_PROTOCOL_PROFILE - int _prof_gtim; +    #endif      // ------------------------      // 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[2]->value)   #define ASN1_DECODE_RESULTREFS(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->elements[3]->elements)   #define ASN1_DECODE_ENTRIES(X) _New_decode(X)
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:99:       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;    private mapping lauth = ([]); +  private object last_rv = 0; // last returned value    -  -  +     //! Contains the result of a LDAP search.    //!    //! @seealso    //! @[LDAP.client.search], @[LDAP.client.result.fetch]    //!    class result // ------------------    {       private int resultcode = LDAP_SUCCESS;    private string resultstring;
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:291:    int next() {    if (actnum < (num_entries()-1)) {    actnum++;    return(count_entries());    }    return(0);    }       } // end of class 'result' ---------------    -  // helper functions +  // helper functions and macros    -  + #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);    }   
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:351:    //! @tt{"ldap://hostname/basedn?attrlist?scope?ext"@}    //!    //! @param context    //! TLS context of connection    //!    //! @seealso    //! @[LDAP.client.bind], @[LDAP.client.search]    void create(string|void url, object|void context)    {    -  info = ([ "code_revision" : ("$Revision: 1.34 $"/" ")[1] ]); +  info = ([ "code_revision" : ("$Revision: 1.35 $"/" ")[1] ]);       if(!url || !sizeof(url))    url = LDAP_DEFAULT_URL;       lauth = parse_url(url);       if(!stringp(lauth->scheme) || -  ((lauth->scheme != "ldap") && (lauth->scheme != "ldaps"))) { +  ((lauth->scheme != "ldap") + #if constant(SSL.sslfile) +  && (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.sslfile)    if(lauth->scheme == "ldaps" && !context) {    context = SSL.context();    // Allow only strong crypto    context->preferred_suites = ({    SSL_rsa_with_idea_cbc_sha,    SSL_rsa_with_rc4_128_sha,    SSL_rsa_with_rc4_128_md5,    SSL_rsa_with_3des_ede_cbc_sha,    });    } -  + #endif       if(!(::connect(lauth->host, lauth->port))) {    //errno = ldapfd->errno();    seterr (LDAP_SERVER_DOWN);    DWRITE("client.create: ERROR: can't open socket.\n");    //ldapfd->destroy();    //ldap=0;    //ok = 0;    //if(con_fail)    // con_fail(this_object(), @extra_args);
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:462:    //! @param version    //! Only @tt{2@} or @tt{3@} can be entered.    //!    //! @note    //! Only simple authentication type is implemented. So be warned    //! clear text passwords are sent to the directory server.    int bind (string|void dn, string|void password, int|void version) {       int id;    mixed raw; -  object rv; +     string pass = password;    password = "censored";       if (!version)    version = LDAP_DEFAULT_VERSION;    if (chk_ver())    return(-ldap_errno);    if (!stringp(dn))    dn = mappingp(lauth->ext) ? lauth->ext->bindname||"" : "";    if (!stringp(pass))
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:484:    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(-ldap_errno);    }    -  rv = result(({raw}),1); -  if (!rv->error_number()) +  binded = 0; +  last_rv = result(({raw}),1); +  if (!last_rv->error_number())    binded = 1; -  DWRITE_HI(sprintf("client.BIND: %s\n", rv->error_string())); -  return (seterr (rv->error_number())); +  DWRITE_HI(sprintf("client.BIND: %s\n", last_rv->error_string())); +  return seterr (last_rv->error_number());       } // bind       private int send_unbind_op() {    // UNBIND operation       writemsg(ASN1_APPLICATION_OCTET_STRING(2, ""));       //ldap::close();       return (1);    }    -  + #if 0    void destroy() {       //send_unbind_op(); -  destruct(this_object()); +  +  // Hazard area: General confusion error. /mast +  //destruct(this_object());    } -  + #endif       //!    //! Unbinds from the directory and close the connection.    int unbind () {       if (send_unbind_op() < 1) {    THROW(({error_string()+"\n",backtrace()}));    return(-ldap_errno);    }    binded = 0;
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:537:       //!    //! Deletes entry from the LDAP server.    //!    //! @param dn    //! The distinguished name of deleted entry.    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) {    dn = string_to_utf8(dn);    }    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())); +  last_rv = result(({raw})); +  DWRITE_HI(sprintf("client.DELETE: %s\n", last_rv->error_string())); +  return seterr (last_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),
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:589:    //!    //! @param dn    //! The distinguished name of compared entry.    //!    //! @param aval    //! The mapping of compared attributes and theirs values.    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) {    dn = string_to_utf8(dn);    aval = Array.map(aval, string_to_utf8);    }    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())); +  last_rv = result(({raw})); +  DWRITE_HI(sprintf("client.COMPARE: %s\n", last_rv->error_string())); +  return seterr (last_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 = ({});   
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:652:       //! The Add Operation allows a client to request the addition    //! of an entry into the directory    //!    //! @param dn    //! The Distinguished Name of the entry to be added.    //!    //! @param attrs    //! The mapping of attributes and their values that make up the content    //! of the entry being added. -  //! +     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) {    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));    }    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())); +  last_rv = result(({raw})); +  DWRITE_HI(sprintf("client.ADD: %s\n", last_rv->error_string())); +  return seterr (last_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++)
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:749:    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]; +  ahlp[-1] = ahlp[-1][..sizeof (ahlp[-1]) - 2] + "*" + 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
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:846:    if (objectp(ohlp = make_filter(filter_get_sub1expr(filter[1..])[0])))    return(ASN1_CONTEXT_SEQUENCE(2, ({ ohlp}) ));    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){ +  private object|int make_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 = ({});    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, +  return 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)); +     }    -  + //! @ignore + IF_ELSE_PAGED_SEARCH(static multiset(string) supported_controls;,) + //! @endignore    -  // API function (ldap_search) -  // -  // search(string filter, int|void attrsonly, array(string)|void attrs) -  // -  // filter: search filter -  // attrsonly: flag -  // attrsy: attribute(s) name -  object|int search (string|void filter, int|void attrsonly, array(string)|void attrs) { +  //! Search LDAP directory. +  //! +  //! @param filter +  //! Search filter used when searching directory objects. +  //! +  //! @param attrs +  //! The array of attribute names which will be returned by server. +  //! for every entry. +  //! +  //! @param attrsonly +  //! The flag causes server return only attribute name but not +  //! the attribute values. +  //! +  //! @note +  //! For syntax of search filter see at RFC 1960 +  //! (http://community.roxen.com/developers/idocs/rfc/rfc1960.html). +  //! +  //! @seealso +  //! @[LDAP.client.result], @[LDAP.client.result.fetch] +  object|int search (string|void filter, array(string)|void attrs, +  int|void attrsonly) {       int id,nv;    mixed raw;    array(string) rawarr = ({}); -  mixed rv; +        filter=filter||lauth->filter; // default from LDAP URI       DWRITE_HI("client.SEARCH: " + (string)filter + "\n");    if (chk_ver())    return(-ldap_errno);    if (chk_binded())    return(-ldap_errno);    if(ldap_version == 3) {    filter = string_to_utf8(filter);    } - #ifdef LDAP_PROTOCOL_PROFILE -  _prof_gtim = gauge{ - #endif -  if(intp(raw = send_search_op(ldap_basedn, ldap_scope, ldap_deref, +  +  object|int search_request; +  +  IF_ELSE_PAGED_SEARCH({ +  if (!supported_controls) { +  // We need to find out if controls are supported. +  PROFILE("supported_controls", { +  supported_controls = (<>); +  search_request = +  make_search_op("", 0, 0, 0, 0, 0, +  "(objectClass=*)", ({"supportedControl"})); +  //werror("search_request: %O\n", search_request); +  if(intp(raw = do_op(search_request))) { +  THROW(({error_string()+"\n",backtrace()})); +  return -ldap_errno; +  } +  do { +  object res = .ldap_privates.ldap_der_decode(raw); +  if (res->elements[1]->get_tag() == 5) break; +  //werror("res: %O\n", res); +  foreach(res->elements[1]->elements[1]->elements, object attr) { +  if (attr->elements[0]->value == "supportedControl") { +  supported_controls |= (< +  @(attr->elements[1]->elements->value) +  >); +  //werror("supported_controls: %O\n", supported_controls); +  } +  } +  if (intp(raw = readmsg(id))) { +  THROW(({error_string()+"\n",backtrace()})); +  return -ldap_errno; +  } +  } while (0); +  }); +  } +  },); +  +  search_request = +  make_search_op(ldap_basedn, ldap_scope, ldap_deref,    ldap_sizelimit, ldap_timelimit, attrsonly, filter, -  attrs||lauth->attributes))) { +  attrs||lauth->attributes); +  +  if(intp(search_request)) {    THROW(({error_string()+"\n",backtrace()}));    return(-ldap_errno);    } - #ifdef LDAP_PROTOCOL_PROFILE -  }; -  DWRITE_PROF("send_search_op: %O\n", _prof_gtim); - #endif +  object cookie = Standards.ASN1.Types.asn1_octet_string(""); +  rawarr = ({}); +  do { +  PROFILE("send_search_op", { +  IF_ELSE_PAGED_SEARCH( +  array ctrls = ({}); +  if (supported_controls["1.2.840.113556.1.4.1339"]) { +  // LDAP_SERVER_DOMAIN_SCOPE_OID +  // "Tells server not to generate referrals" (NtLdap.h) +  ctrls += ({ +  Standards.ASN1.Types.asn1_sequence(({ +  // controlType +  Standards.ASN1.Types.asn1_octet_string("1.2.840.113556.1.4.1339"), +  ASN1_BOOLEAN(0), // criticality (FALSE) +  // controlValue +  Standards.ASN1.Types.asn1_octet_string(""), +  })), +  }); +  } +  if (supported_controls["1.2.840.113556.1.4.319"]) { +  // LDAP Control Extension for Simple Paged Results Manipulation +  // RFC 2696. +  ctrls += ({ +  Standards.ASN1.Types.asn1_sequence(({ +  // controlType +  Standards.ASN1.Types.asn1_octet_string("1.2.840.113556.1.4.319"), +  ASN1_BOOLEAN(sizeof(cookie->value)?0:0xff), // criticality +  // controlValue +  Standards.ASN1.Types.asn1_octet_string( +  Standards.ASN1.Types.asn1_sequence(({ +  // size +  Standards.ASN1.Types.asn1_integer(0x7fffffff), +  cookie, // cookie +  }))->get_der()), +  })), +  }); +  } +  object controls; +  if (sizeof(ctrls)) { +  controls = .ldap_privates.asn1_sequence(0, ctrls); +  } +  ,);    -  rawarr = ({raw}); - #ifdef LDAP_PROTOCOL_PROFILE -  _prof_gtim = gauge{ - #endif +  if(intp(raw = do_op(search_request, +  IF_ELSE_PAGED_SEARCH(controls, 0)))) { +  THROW(({error_string()+"\n",backtrace()})); +  return -ldap_errno; +  } +  }); +  +  PROFILE("rawarr++", { +  rawarr += ({raw});    while (ASN1_DECODE_RESULTAPP(raw) != 5) { - #ifdef LDAP_PROTOCOL_PROFILEx -  DWRITE_PROF("readmsg: %O\n", gauge { raw = readmsg(id); }); - #else -  raw = readmsg(id); - #endif +  PROFILE("readmsg", raw = readmsg(id));    if (intp(raw)) {    THROW(({error_string()+"\n",backtrace()}));    return(-ldap_errno);    }    rawarr += ({raw});    } // while - #ifdef LDAP_PROTOCOL_PROFILE -  }; -  DWRITE_PROF("rawarr++: %O\n", _prof_gtim); - #endif +  });    - #ifdef LDAP_PROTOCOL_PROFILE -  _prof_gtim = gauge{ rv = result(rawarr); }; -  DWRITE_PROF("result: %O\n", _prof_gtim); - #else -  rv = result(rawarr); - #endif -  if(objectp(rv)) -  seterr (rv->error_number()); +  +  // At this point @[raw] contains a SearchResultDone. +  cookie = 0; +  IF_ELSE_PAGED_SEARCH({ +  if ((ASN1_DECODE_RESULTCODE(raw) != 10) && +  (sizeof(.ldap_privates.ldap_der_decode(raw)->elements) > 2)) { +  object controls = .ldap_privates.ldap_der_decode(raw)->elements[2]; +  foreach(controls->elements, object control) { +  if (!control->constructed || +  !sizeof(control) || +  control->elements[0]->type_name != "OCTET STRING") { +  //werror("Protocol error in control %O\n", control); +  // FIXME: Fail? +  continue; +  } +  if (control->elements[0]->value != "1.2.840.113556.1.4.319") { +  //werror("Unknown control %O\n", control->elements[0]->value); +  // FIXME: Should look at criticallity flag. +  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") { +  // FIXME: Error? +  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") { +  // Unexpected control information. +  continue; +  } +  if (sizeof(control_info->elements[1]->value)) { +  cookie = control_info->elements[1]; +  } +  } +  if (cookie) { +  // Remove the extra end marker. +  rawarr = rawarr[..sizeof(rawarr)-2]; +  } +  } +  +  },); +  } while (cookie); +  +  PROFILE("result", last_rv = result(rawarr)); +  if(objectp(last_rv)) +  seterr (last_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); +  DWRITE_HI(sprintf("client.SEARCH: %s (entries: %d)\n", +  last_rv->error_string(), last_rv->num_entries())); +  return(last_rv);       } // search       -  // API function (ldap_setbasedn) -  // -  // set_basedn(string base_dn) -  // -  // base_dn: base DN for search +  //! @param base_dn +  //! base DN for search    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);
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1002:    }    else    if(scope != 0 && scope != 1 && scope != 2)    return (-1);       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 +  //! @param option_type +  //! LDAP_OPT_xxx +  //! @param 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;
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1038:    // 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 +  //! @param 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
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1137:    //! from the entry.    //!    //! @param newsuperior    //! If present, this is the Distinguished Name of the entry    //! which becomes the immediate superior of the existing entry.    //!    int modifydn (string dn, string newrdn, int deleteoldrdn,    string|void newsuperior) {       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) {    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(-ldap_errno);    }    -  rv = result(({raw})); -  DWRITE_HI(sprintf("client.MODIFYDN: %s\n", rv->error_string())); -  return (seterr (rv->error_number())); +  last_rv = result(({raw})); +  DWRITE_HI(sprintf("client.MODIFYDN: %s\n", last_rv->error_string())); +  return seterr (last_rv->error_number());       } //modifydn       //! The Modify Operation allows a client to request that a modification    //! of an entry be performed on its behalf by a server.    //!    //! @param dn    //! The distinguished name of modified entry.    //!    //! @param attropval
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1191:    //! 2 (LDAP_OPERATION_REPLACE) -    //! replace all existing values of the given attribute with the new    //! values listed, creating the attribute if it did not already exist.    //! A replace with no value will delete the entire attribute if it    //! exists, and is ignored if the attribute does not exist    //!    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) {    dn = string_to_utf8(dn);    array(string) keys = indices(attropval);
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1216:    (stringp(x)?    string_to_utf8(x) :    x);    }));    }    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())); +  last_rv = result(({raw})); +  DWRITE_HI(sprintf("client.MODIFY: %s\n", last_rv->error_string())); +  return seterr (last_rv->error_number());       } // modify    -  +  //! Gets referrals. +  //! +  //! @returns +  //! Returns array of referrals or @tt{0@}. +  array|int get_referrals() { +  if(last_rv->referrals) +  return last_rv->referrals; +  return 0; +  }    -  // API function -  // -  // parse_url(string ldapuri) -  // -  // ldapuri: LDAP URL +  //! @param 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)..];