root/usr/src/lib/libldap5/sources/ldap/common/error.c
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#include "ldap-int.h"

struct ldaperror {
        int     e_code;
        char    *e_reason;
};

#ifdef _SOLARIS_SDK
#include <synch.h>
static struct ldaperror ldap_errlist[] = {
        { LDAP_SUCCESS,                         0 },
        { LDAP_OPERATIONS_ERROR,                0 },
        { LDAP_PROTOCOL_ERROR,                  0 },
        { LDAP_TIMELIMIT_EXCEEDED,              0 },
        { LDAP_SIZELIMIT_EXCEEDED,              0 },
        { LDAP_COMPARE_FALSE,                   0 },
        { LDAP_COMPARE_TRUE,                    0 },
        { LDAP_STRONG_AUTH_NOT_SUPPORTED,       0 },
        { LDAP_STRONG_AUTH_REQUIRED,            0 },
        { LDAP_PARTIAL_RESULTS,                 0 },
        { LDAP_REFERRAL,                        0 },
        { LDAP_ADMINLIMIT_EXCEEDED,             0 },
        { LDAP_UNAVAILABLE_CRITICAL_EXTENSION,  0 },
        { LDAP_CONFIDENTIALITY_REQUIRED,        0 },
        { LDAP_SASL_BIND_IN_PROGRESS,           0 },

        { LDAP_NO_SUCH_ATTRIBUTE,               0 },
        { LDAP_UNDEFINED_TYPE,                  0 },
        { LDAP_INAPPROPRIATE_MATCHING,          0 },
        { LDAP_CONSTRAINT_VIOLATION,            0 },
        { LDAP_TYPE_OR_VALUE_EXISTS,            0 },
        { LDAP_INVALID_SYNTAX,                  0 },

        { LDAP_NO_SUCH_OBJECT,                  0 },
        { LDAP_ALIAS_PROBLEM,                   0 },
        { LDAP_INVALID_DN_SYNTAX,               0 },
        { LDAP_IS_LEAF,                         0 },
        { LDAP_ALIAS_DEREF_PROBLEM,             0 },

        { LDAP_INAPPROPRIATE_AUTH,              0 },
        { LDAP_INVALID_CREDENTIALS,             0 },
        { LDAP_INSUFFICIENT_ACCESS,             0 },
        { LDAP_BUSY,                            0 },
        { LDAP_UNAVAILABLE,                     0 },
        { LDAP_UNWILLING_TO_PERFORM,            0 },
        { LDAP_LOOP_DETECT,                     0 },
        { LDAP_SORT_CONTROL_MISSING,            0 },
        { LDAP_INDEX_RANGE_ERROR,               0 },

        { LDAP_NAMING_VIOLATION,                0 },
        { LDAP_OBJECT_CLASS_VIOLATION,          0 },
        { LDAP_NOT_ALLOWED_ON_NONLEAF,          0 },
        { LDAP_NOT_ALLOWED_ON_RDN,              0 },
        { LDAP_ALREADY_EXISTS,                  0 },
        { LDAP_NO_OBJECT_CLASS_MODS,            0 },
        { LDAP_RESULTS_TOO_LARGE,               0 },
        { LDAP_AFFECTS_MULTIPLE_DSAS,           0 },

        { LDAP_OTHER,                           0 },
        { LDAP_SERVER_DOWN,                     0 },
        { LDAP_LOCAL_ERROR,                     0 },
        { LDAP_ENCODING_ERROR,                  0 },
        { LDAP_DECODING_ERROR,                  0 },
        { LDAP_TIMEOUT,                         0 },
        { LDAP_AUTH_UNKNOWN,                    0 },
        { LDAP_FILTER_ERROR,                    0 },
        { LDAP_USER_CANCELLED,                  0 },
        { LDAP_PARAM_ERROR,                     0 },
        { LDAP_NO_MEMORY,                       0 },
        { LDAP_CONNECT_ERROR,                   0 },
        { LDAP_NOT_SUPPORTED,                   0 },
        { LDAP_CONTROL_NOT_FOUND,               0 },
        { LDAP_NO_RESULTS_RETURNED,             0 },
        { LDAP_MORE_RESULTS_TO_RETURN,          0 },
        { LDAP_CLIENT_LOOP,                     0 },
        { LDAP_REFERRAL_LIMIT_EXCEEDED,         0 },
        { -1, 0 }
};
const int last_index = sizeof(ldap_errlist)/sizeof(ldap_errlist[0]) - 2;
#else
static struct ldaperror ldap_errlist[] = {
        { LDAP_SUCCESS,                         "Success" },
        { LDAP_OPERATIONS_ERROR,                "Operations error" },
        { LDAP_PROTOCOL_ERROR,                  "Protocol error" },
        { LDAP_TIMELIMIT_EXCEEDED,              "Timelimit exceeded" },
        { LDAP_SIZELIMIT_EXCEEDED,              "Sizelimit exceeded" },
        { LDAP_COMPARE_FALSE,                   "Compare false" },
        { LDAP_COMPARE_TRUE,                    "Compare true" },
        { LDAP_STRONG_AUTH_NOT_SUPPORTED,       "Authentication method not supported" },
        { LDAP_STRONG_AUTH_REQUIRED,            "Strong authentication required" },
        { LDAP_PARTIAL_RESULTS,                 "Partial results and referral received" },
        { LDAP_REFERRAL,                        "Referral received" },
        { LDAP_ADMINLIMIT_EXCEEDED,             "Administrative limit exceeded" },
        { LDAP_UNAVAILABLE_CRITICAL_EXTENSION,  "Unavailable critical extension" },
        { LDAP_CONFIDENTIALITY_REQUIRED,        "Confidentiality required" },
        { LDAP_SASL_BIND_IN_PROGRESS,           "SASL bind in progress" },

        { LDAP_NO_SUCH_ATTRIBUTE,               "No such attribute" },
        { LDAP_UNDEFINED_TYPE,                  "Undefined attribute type" },
        { LDAP_INAPPROPRIATE_MATCHING,          "Inappropriate matching" },
        { LDAP_CONSTRAINT_VIOLATION,            "Constraint violation" },
        { LDAP_TYPE_OR_VALUE_EXISTS,            "Type or value exists" },
        { LDAP_INVALID_SYNTAX,                  "Invalid syntax" },

        { LDAP_NO_SUCH_OBJECT,                  "No such object" },
        { LDAP_ALIAS_PROBLEM,                   "Alias problem" },
        { LDAP_INVALID_DN_SYNTAX,               "Invalid DN syntax" },
        { LDAP_IS_LEAF,                         "Object is a leaf" },
        { LDAP_ALIAS_DEREF_PROBLEM,             "Alias dereferencing problem" },

        { LDAP_INAPPROPRIATE_AUTH,              "Inappropriate authentication" },
        { LDAP_INVALID_CREDENTIALS,             "Invalid credentials" },
        { LDAP_INSUFFICIENT_ACCESS,             "Insufficient access" },
        { LDAP_BUSY,                            "DSA is busy" },
        { LDAP_UNAVAILABLE,                     "DSA is unavailable" },
        { LDAP_UNWILLING_TO_PERFORM,            "DSA is unwilling to perform" },
        { LDAP_LOOP_DETECT,                     "Loop detected" },
    { LDAP_SORT_CONTROL_MISSING,    "Sort Control is missing"  },
    { LDAP_INDEX_RANGE_ERROR,              "Search results exceed the range specified by the offsets" },

    { LDAP_NAMING_VIOLATION,            "Naming violation" },
        { LDAP_OBJECT_CLASS_VIOLATION,          "Object class violation" },
        { LDAP_NOT_ALLOWED_ON_NONLEAF,          "Operation not allowed on nonleaf" },
        { LDAP_NOT_ALLOWED_ON_RDN,              "Operation not allowed on RDN" },
        { LDAP_ALREADY_EXISTS,                  "Already exists" },
        { LDAP_NO_OBJECT_CLASS_MODS,            "Cannot modify object class" },
        { LDAP_RESULTS_TOO_LARGE,               "Results too large" },
        { LDAP_AFFECTS_MULTIPLE_DSAS,           "Affects multiple servers" },

        { LDAP_OTHER,                           "Unknown error" },
        { LDAP_SERVER_DOWN,                     "Can't contact LDAP server" },
        { LDAP_LOCAL_ERROR,                     "Local error" },
        { LDAP_ENCODING_ERROR,                  "Encoding error" },
        { LDAP_DECODING_ERROR,                  "Decoding error" },
        { LDAP_TIMEOUT,                         "Timed out" },
        { LDAP_AUTH_UNKNOWN,                    "Unknown authentication method" },
        { LDAP_FILTER_ERROR,                    "Bad search filter" },
        { LDAP_USER_CANCELLED,                  "User cancelled operation" },
        { LDAP_PARAM_ERROR,                     "Bad parameter to an ldap routine" },
        { LDAP_NO_MEMORY,                       "Out of memory" },
        { LDAP_CONNECT_ERROR,                   "Can't connect to the LDAP server" },
        { LDAP_NOT_SUPPORTED,                   "Not supported by this version of the LDAP protocol" },
        { LDAP_CONTROL_NOT_FOUND,               "Requested LDAP control not found" },
        { LDAP_NO_RESULTS_RETURNED,             "No results returned" },
        { LDAP_MORE_RESULTS_TO_RETURN,          "More results to return" },
        { LDAP_CLIENT_LOOP,                     "Client detected loop" },
        { LDAP_REFERRAL_LIMIT_EXCEEDED,         "Referral hop limit exceeded" },
        { -1, 0 }
};
#endif

#ifdef _SOLARIS_SDK
static mutex_t          err_mutex = DEFAULTMUTEX;

static void fill_ldap_errlist()
{
        int i=0;
        mutex_lock(&err_mutex);

        LDAPDebug(LDAP_DEBUG_TRACE, "fill_ldap_errlist\n", 0, 0, 0 );

        if (ldap_errlist[last_index].e_reason != NULL) {
                mutex_unlock(&err_mutex);
                return;
        }

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Success");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Operations error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Protocol error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Timelimit exceeded");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Sizelimit exceeded");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Compare false");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Compare true");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Authentication method not supported");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Strong authentication required");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Partial results and referral received");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Referral received");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Administrative limit exceeded");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Unavailable critical extension");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Confidentiality required");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "SASL bind in progress");

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "No such attribute");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Undefined attribute type");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Inappropriate matching");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Constraint violation");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Type or value exists");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Invalid syntax");

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "No such object");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Alias problem");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Invalid DN syntax");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Object is a leaf");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Alias dereferencing problem");

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Inappropriate authentication");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Invalid credentials");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Insufficient access");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "DSA is busy");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "DSA is unavailable");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "DSA is unwilling to perform");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Loop detected");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Sort Control is missing");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                "Search results exceed the range specified by the offsets");

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Naming violation");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Object class violation");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Operation not allowed on nonleaf");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Operation not allowed on RDN");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Already exists");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Cannot modify object class");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Results too large");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Affects multiple servers");

        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Unknown error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Can't contact LDAP server");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Local error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Encoding error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Decoding error");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Timed out");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Unknown authentication method");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Bad search filter");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "User cancelled operation");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Bad parameter to an ldap routine");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN, "Out of memory");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Can't connect to the LDAP server");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                        "Not supported by this version of the LDAP protocol");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Requested LDAP control not found");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "No results returned");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "More results to return");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Client detected loop");
        ldap_errlist[i++].e_reason = dgettext(TEXT_DOMAIN,
                                "Referral hop limit exceeded");
        mutex_unlock(&err_mutex);
}
#endif

char *
LDAP_CALL
ldap_err2string( int err )
{
        int     i;

        LDAPDebug( LDAP_DEBUG_TRACE, "ldap_err2string\n", 0, 0, 0 );

#ifdef _SOLARIS_SDK
        /* Make sure errlist is initialized before referencing err string */
        if (ldap_errlist[last_index].e_reason == NULL)
                fill_ldap_errlist();
#endif

        for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) {
                if ( err == ldap_errlist[i].e_code )
                        return( ldap_errlist[i].e_reason );
        }

        return( dgettext(TEXT_DOMAIN, "Unknown error") );
}


static char *
nsldapi_safe_strerror( int e )
{
        char *s;

        if (( s = strerror( e )) == NULL ) {
                s = dgettext(TEXT_DOMAIN, "unknown error");
        }

        return( s );
}


void
LDAP_CALL
ldap_perror( LDAP *ld, const char *s )
{
        int     i, err;
        char    *matched, *errmsg, *separator;
        char    msg[1024];

        LDAPDebug( LDAP_DEBUG_TRACE, "ldap_perror\n", 0, 0, 0 );

#ifdef _SOLARIS_SDK
        /* Make sure errlist is initialized before referencing err string */
        if (ldap_errlist[last_index].e_reason == NULL)
                fill_ldap_errlist();
#endif

        if ( s == NULL ) {
                s = separator = "";
        } else {
                separator = ": ";
        }

        if ( ld == NULL ) {
                sprintf( msg, "%s%s%s", s, separator,
                    nsldapi_safe_strerror( errno ) );
                ber_err_print( msg );
                return;
        }

        LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK );
        err = LDAP_GET_LDERRNO( ld, &matched, &errmsg );
        for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) {
                if ( err == ldap_errlist[i].e_code ) {
                        sprintf( msg, "%s%s%s", s, separator,
                                    ldap_errlist[i].e_reason );
                        ber_err_print( msg );
                        if ( err == LDAP_CONNECT_ERROR ) {
                                ber_err_print( " - " );
                                ber_err_print( nsldapi_safe_strerror(
                                    LDAP_GET_ERRNO( ld )));
                        }
                        ber_err_print( "\n" );
                        if ( matched != NULL && *matched != '\0' ) {
                                sprintf( msg, dgettext(TEXT_DOMAIN,
                                        "%s%smatched: %s\n"),
                                    s, separator, matched );
                                ber_err_print( msg );
                        }
                        if ( errmsg != NULL && *errmsg != '\0' ) {
                                sprintf( msg, dgettext(TEXT_DOMAIN,
                                        "%s%sadditional info: %s\n"),
                                    s, separator, errmsg );
                                ber_err_print( msg );
                        }
                        LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
                        return;
                }
        }
        sprintf( msg, dgettext(TEXT_DOMAIN, "%s%sNot an LDAP errno %d\n"),
                s, separator, err );
        ber_err_print( msg );
        LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
}

int
LDAP_CALL
ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit )
{
        int     lderr_parse, lderr;

        lderr_parse = ldap_parse_result( ld, r, &lderr, NULL, NULL, NULL,
            NULL, freeit );

        if ( lderr_parse != LDAP_SUCCESS ) {
                return( lderr_parse );
        }

        return( lderr );
}

int
LDAP_CALL
ldap_get_lderrno( LDAP *ld, char **m, char **s )
{
        if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
                return( LDAP_PARAM_ERROR );     /* punt */
        }

        if ( ld->ld_get_lderrno_fn == NULL ) {
                if ( m != NULL ) {
                        *m = ld->ld_matched;
                }
                if ( s != NULL ) {
                        *s = ld->ld_error;
                }
                return( ld->ld_errno );
        } else {
                return( ld->ld_get_lderrno_fn( m, s, ld->ld_lderrno_arg ) );
        }
}


/*
 * Note: there is no need for callers of ldap_set_lderrno() to lock the
 * ld mutex.  If applications intend to share an LDAP session handle
 * between threads they *must* perform their own locking around the
 * session handle or they must install a "set lderrno" thread callback
 * function.
 *
 */
int
LDAP_CALL
ldap_set_lderrno( LDAP *ld, int e, char *m, char *s )
{
        if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
                return( LDAP_PARAM_ERROR );
        }

        if ( ld->ld_set_lderrno_fn != NULL ) {
                ld->ld_set_lderrno_fn( e, m, s, ld->ld_lderrno_arg );
        } else {
        LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK );
                ld->ld_errno = e;
                if ( ld->ld_matched ) {
                        NSLDAPI_FREE( ld->ld_matched );
                }
                ld->ld_matched = m;
                if ( ld->ld_error ) {
                        NSLDAPI_FREE( ld->ld_error );
                }
                ld->ld_error = s;
        LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK );
        }

        return( LDAP_SUCCESS );
}


/*
 * Returns an LDAP error that says whether parse succeeded.  The error code
 * from the LDAP result itself is returned in the errcodep result parameter.
 * If any of the result params. (errcodep, matchednp, errmsgp, referralsp,
 * or serverctrlsp) are NULL we don't return that info.
 */
int
LDAP_CALL
ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep, char **matchednp,
        char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp,
        int freeit )
{
        LDAPMessage             *lm;
        int                     err, errcode;
        char                    *m, *e;

        LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 );

        if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ||
            !NSLDAPI_VALID_LDAPMESSAGE_POINTER( res )) {
                return( LDAP_PARAM_ERROR );
        }

        /* skip over entries and references to find next result in this chain */
        for ( lm = res; lm != NULL; lm = lm->lm_chain ) {
                if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
                    lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
                        break;
                }
        }

        if ( lm == NULL ) {
                err = LDAP_NO_RESULTS_RETURNED;
                LDAP_SET_LDERRNO( ld, err, NULL, NULL );
                return( err );
        }

        err = nsldapi_parse_result( ld, lm->lm_msgtype, lm->lm_ber, &errcode,
            &m, &e, referralsp, serverctrlsp );

        if ( err == LDAP_SUCCESS ) {
                if ( errcodep != NULL ) {
                        *errcodep = errcode;
                }
                if ( matchednp != NULL ) {
                        *matchednp = nsldapi_strdup( m );
                }
                if ( errmsgp != NULL ) {
                        *errmsgp = nsldapi_strdup( e );
                }

                /*
                 * if there are more result messages in the chain, arrange to
                 * return the special LDAP_MORE_RESULTS_TO_RETURN "error" code.
                 */
                for ( lm = lm->lm_chain; lm != NULL; lm = lm->lm_chain ) {
                        if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
                            lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
                                err = LDAP_MORE_RESULTS_TO_RETURN;
                                break;
                        }
                }
        } else {
                m = e = NULL;
        }

        if ( freeit ) {
                ldap_msgfree( res );
        }

        LDAP_SET_LDERRNO( ld, ( err == LDAP_SUCCESS ) ? errcode : err, m, e );

        return( err );
}


/*
 * returns an LDAP error code indicating success or failure of parsing
 * does NOT set any error information inside "ld"
 */
int
nsldapi_parse_result( LDAP *ld, int msgtype, BerElement *rber, int *errcodep,
    char **matchednp, char **errmsgp, char ***referralsp,
    LDAPControl ***serverctrlsp )
{
        BerElement      ber;
        ber_len_t       len;
        int             berrc, err, errcode;
        ber_int_t       along;
        char            *m, *e;

        /*
         * Parse the result message.  LDAPv3 result messages look like this:
         *
         *      LDAPResult ::= SEQUENCE {
         *              resultCode      ENUMERATED { ... },
         *              matchedDN       LDAPDN,
         *              errorMessage    LDAPString,
         *              referral        [3] Referral OPTIONAL
         *              opSpecificStuff OPTIONAL
         *      }
         *
         * all wrapped up in an LDAPMessage sequence which looks like this:
         *      LDAPMessage ::= SEQUENCE {
         *              messageID       MessageID,
         *              LDAPResult      CHOICE { ... }, // message type
         *              controls        [0] Controls OPTIONAL
         *      }
         *
         * LDAPv2 messages don't include referrals or controls.
         * LDAPv1 messages don't include matchedDN, referrals, or controls.
         *
         * ldap_result() pulls out the message id, so by the time a result
         * message gets here we are sitting at the start of the LDAPResult.
         */

        err = LDAP_SUCCESS;     /* optimistic */
        m = e = NULL;
        if ( matchednp != NULL ) {
                *matchednp = NULL;
        }
        if ( errmsgp != NULL ) {
                *errmsgp = NULL;
        }
        if ( referralsp != NULL ) {
                *referralsp = NULL;
        }
        if ( serverctrlsp != NULL ) {
                *serverctrlsp = NULL;
        }
        ber = *rber;            /* struct copy */

        if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION2 ) {
                berrc = ber_scanf( &ber, "{ia}", &along, &e );
                errcode = (int)along;   /* XXX lossy cast */
        } else {
                if (( berrc = ber_scanf( &ber, "{iaa", &along, &m, &e ))
                    != LBER_ERROR ) {
                        errcode = (int)along;   /* XXX lossy cast */
                        /* check for optional referrals */
                        if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_REFERRAL ) {
                                if ( referralsp == NULL ) {
                                        /* skip referrals */
                                        berrc = ber_scanf( &ber, "x" );
                                } else {
                                        /* suck out referrals */
                                        berrc = ber_scanf( &ber, "v",
                                            referralsp );
                                }
                        } else if ( referralsp != NULL ) {
                                *referralsp = NULL;
                        }
                }

                if ( berrc != LBER_ERROR ) {
                        /*
                         * skip past optional operation-specific elements:
                         *   bind results - serverSASLcreds
                         *   extendedop results -  OID plus value
                         */
                        if ( msgtype == LDAP_RES_BIND ) {
                                if ( ber_peek_tag( &ber, &len ) ==
                                    LDAP_TAG_SASL_RES_CREDS ) {
                                        berrc = ber_scanf( &ber, "x" );
                                }
                        } else if ( msgtype == LDAP_RES_EXTENDED ) {
                                if ( ber_peek_tag( &ber, &len ) ==
                                    LDAP_TAG_EXOP_RES_OID ) {
                                        berrc = ber_scanf( &ber, "x" );
                                }
                                if ( berrc != LBER_ERROR &&
                                    ber_peek_tag( &ber, &len ) ==
                                    LDAP_TAG_EXOP_RES_VALUE ) {
                                        berrc = ber_scanf( &ber, "x" );
                                }
                        }
                }

                /* pull out controls (if requested and any are present) */
                if ( berrc != LBER_ERROR && serverctrlsp != NULL &&
                    ( berrc = ber_scanf( &ber, "}" )) != LBER_ERROR ) {
                        err = nsldapi_get_controls( &ber, serverctrlsp );
                }
        }

        if ( berrc == LBER_ERROR && err == LDAP_SUCCESS ) {
                err = LDAP_DECODING_ERROR;
        }

        if ( errcodep != NULL ) {
                *errcodep = errcode;
        }
        if ( matchednp != NULL ) {
                *matchednp = m;
        } else if ( m != NULL ) {
                NSLDAPI_FREE( m );
        }
        if ( errmsgp != NULL ) {
                *errmsgp = e;
        } else if ( e != NULL ) {
                NSLDAPI_FREE( e );
        }

        return( err );
}