/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsLDAPInternal.h" #include "nsLDAPMessage.h" #include "nspr.h" #include "nsDebug.h" #include "nsMemory.h" #include "nsLDAPConnection.h" #include "nsISupportsUtils.h" #include "nsLDAPBERValue.h" #include "nsILDAPErrors.h" #include "nsIClassInfoImpl.h" #include "nsLDAPUtils.h" #include "mozilla/Logging.h" NS_IMPL_CLASSINFO(nsLDAPMessage, NULL, nsIClassInfo::THREADSAFE, NS_LDAPMESSAGE_CID) NS_IMPL_ADDREF(nsLDAPMessage) NS_IMPL_RELEASE(nsLDAPMessage) NS_INTERFACE_MAP_BEGIN(nsLDAPMessage) NS_INTERFACE_MAP_ENTRY(nsILDAPMessage) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPMessage) NS_IMPL_QUERY_CLASSINFO(nsLDAPMessage) NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_CI_INTERFACE_GETTER(nsLDAPMessage, nsILDAPMessage) // constructor // nsLDAPMessage::nsLDAPMessage() : mMsgHandle(0), mErrorCode(LDAP_SUCCESS), mMatchedDn(0), mErrorMessage(0), mReferrals(0), mServerControls(0) { } // destructor // nsLDAPMessage::~nsLDAPMessage(void) { if (mMsgHandle) { int rc = ldap_msgfree(mMsgHandle); // If you are having problems compiling the following code on a Solaris // machine with the Forte 6 Update 1 compilers, then you need to make // sure you have applied all the required patches. See: // http://www.mozilla.org/unix/solaris-build.html for more details. switch(rc) { case LDAP_RES_BIND: case LDAP_RES_SEARCH_ENTRY: case LDAP_RES_SEARCH_RESULT: case LDAP_RES_MODIFY: case LDAP_RES_ADD: case LDAP_RES_DELETE: case LDAP_RES_MODRDN: case LDAP_RES_COMPARE: case LDAP_RES_SEARCH_REFERENCE: case LDAP_RES_EXTENDED: case LDAP_RES_ANY: // success break; case LDAP_SUCCESS: // timed out (dunno why LDAP_SUCCESS is used to indicate this) MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() " "timed out\n")); break; default: // other failure MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() " "failed: %s\n", ldap_err2string(rc))); break; } } if (mMatchedDn) { ldap_memfree(mMatchedDn); } if (mErrorMessage) { ldap_memfree(mErrorMessage); } if (mReferrals) { ldap_value_free(mReferrals); } if (mServerControls) { ldap_controls_free(mServerControls); } } /** * Initializes a message. * * @param aConnection The nsLDAPConnection this message is on * @param aMsgHandle The native LDAPMessage to be wrapped. * * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in * @exception NS_ERROR_UNEXPECTED internal err; shouldn't happen * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER decoding * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory */ nsresult nsLDAPMessage::Init(nsILDAPConnection *aConnection, LDAPMessage *aMsgHandle) { int parseResult; if (!aConnection || !aMsgHandle) { NS_WARNING("Null pointer passed in to nsLDAPMessage::Init()"); return NS_ERROR_ILLEGAL_VALUE; } // initialize the appropriate member vars // mConnection = aConnection; mMsgHandle = aMsgHandle; // cache the connection handle. we're violating the XPCOM type-system // here since we're a friend of the connection class and in the // same module. // mConnectionHandle = static_cast(aConnection)->mConnectionHandle; // do any useful message parsing // const int msgType = ldap_msgtype(mMsgHandle); if ( msgType == -1) { NS_ERROR("nsLDAPMessage::Init(): ldap_msgtype() failed"); return NS_ERROR_UNEXPECTED; } switch (msgType) { case LDAP_RES_SEARCH_REFERENCE: // XXX should do something here? break; case LDAP_RES_SEARCH_ENTRY: // nothing to do here break; case LDAP_RES_EXTENDED: // XXX should do something here? break; case LDAP_RES_BIND: case LDAP_RES_SEARCH_RESULT: case LDAP_RES_MODIFY: case LDAP_RES_ADD: case LDAP_RES_DELETE: case LDAP_RES_MODRDN: case LDAP_RES_COMPARE: parseResult = ldap_parse_result(mConnectionHandle, mMsgHandle, &mErrorCode, &mMatchedDn, &mErrorMessage,&mReferrals, &mServerControls, 0); switch (parseResult) { case LDAP_SUCCESS: // we're good break; case LDAP_DECODING_ERROR: NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() hit a " "decoding error"); return NS_ERROR_LDAP_DECODING_ERROR; case LDAP_NO_MEMORY: NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() ran out " "of memory"); return NS_ERROR_OUT_OF_MEMORY; case LDAP_PARAM_ERROR: case LDAP_MORE_RESULTS_TO_RETURN: case LDAP_NO_RESULTS_RETURNED: default: NS_ERROR("nsLDAPMessage::Init(): ldap_parse_result returned " "unexpected return code"); return NS_ERROR_UNEXPECTED; } break; default: NS_ERROR("nsLDAPMessage::Init(): unexpected message type"); return NS_ERROR_UNEXPECTED; } return NS_OK; } /** * The result code of the (possibly partial) operation. * * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in * * readonly attribute long errorCode; */ NS_IMETHODIMP nsLDAPMessage::GetErrorCode(int32_t *aErrorCode) { if (!aErrorCode) { return NS_ERROR_ILLEGAL_VALUE; } *aErrorCode = mErrorCode; return NS_OK; } NS_IMETHODIMP nsLDAPMessage::GetType(int32_t *aType) { if (!aType) { return NS_ERROR_ILLEGAL_VALUE; } *aType = ldap_msgtype(mMsgHandle); if (*aType == -1) { return NS_ERROR_UNEXPECTED; }; return NS_OK; } // we don't get to use exceptions, so we'll fake it. this is an error // handler for IterateAttributes(). // nsresult nsLDAPMessage::IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount, char** *aAttributes, BerElement *position) { // if necessary, free the position holder used by // ldap_{first,next}_attribute() // if (position) { ldap_ber_free(position, 0); } // deallocate any entries in the array that have been allocated, then // the array itself // if (*aAttributes) { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aAttrCount, *aAttributes); } // possibly spit out a debugging message, then return an appropriate // error code // switch (aLderrno) { case LDAP_PARAM_ERROR: NS_WARNING("nsLDAPMessage::IterateAttributes() failure; probable bug " "or memory corruption encountered"); return NS_ERROR_UNEXPECTED; break; case LDAP_DECODING_ERROR: NS_WARNING("nsLDAPMessage::IterateAttributes(): decoding error"); return NS_ERROR_LDAP_DECODING_ERROR; break; case LDAP_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; break; } NS_WARNING("nsLDAPMessage::IterateAttributes(): LDAP C SDK returned " "unexpected value; possible bug or memory corruption"); return NS_ERROR_UNEXPECTED; } // wrapper for ldap_first_attribute // NS_IMETHODIMP nsLDAPMessage::GetAttributes(uint32_t *aAttrCount, char** *aAttributes) { return IterateAttributes(aAttrCount, aAttributes, true); } // if getP is true, we get the attributes by recursing once // (without getP set) in order to fill in *attrCount, then allocate // and fill in the *aAttributes. // // if getP is false, just fill in *attrCount and return // nsresult nsLDAPMessage::IterateAttributes(uint32_t *aAttrCount, char** *aAttributes, bool getP) { BerElement *position; nsresult rv; if (!aAttrCount || !aAttributes ) { return NS_ERROR_INVALID_POINTER; } // if we've been called from GetAttributes, recurse once in order to // count the elements in this message. // if (getP) { *aAttributes = 0; *aAttrCount = 0; rv = IterateAttributes(aAttrCount, aAttributes, false); if (NS_FAILED(rv)) return rv; // create an array of the appropriate size // *aAttributes = static_cast(moz_xmalloc(*aAttrCount * sizeof(char *))); if (!*aAttributes) { return NS_ERROR_OUT_OF_MEMORY; } } // get the first attribute // char *attr = ldap_first_attribute(mConnectionHandle, mMsgHandle, &position); if (!attr) { return IterateAttrErrHandler(ldap_get_lderrno(mConnectionHandle, 0, 0), aAttrCount, aAttributes, position); } // if we're getting attributes, try and fill in the first field // if (getP) { (*aAttributes)[0] = NS_strdup(attr); if (!(*aAttributes)[0]) { ldap_memfree(attr); free(*aAttributes); return NS_ERROR_OUT_OF_MEMORY; } // note that we start counting again, in order to keep our place in // the array so that we can unwind gracefully and avoid leakage if // we hit an error as we're filling in the array // *aAttrCount = 1; } else { // otherwise just update the count // *aAttrCount = 1; } ldap_memfree(attr); while (1) { // get the next attribute // attr = ldap_next_attribute(mConnectionHandle, mMsgHandle, position); // check to see if there is an error, or if we're just done iterating // if (!attr) { // bail out if there's an error // int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); if (lderrno != LDAP_SUCCESS) { return IterateAttrErrHandler(lderrno, aAttrCount, aAttributes, position); } // otherwise, there are no more attributes; we're done with // the while loop // break; } else if (getP) { // if ldap_next_attribute did return successfully, and // we're supposed to fill in a value, do so. // (*aAttributes)[*aAttrCount] = NS_strdup(attr); if (!(*aAttributes)[*aAttrCount]) { ldap_memfree(attr); return IterateAttrErrHandler(LDAP_NO_MEMORY, aAttrCount, aAttributes, position); } } ldap_memfree(attr); // we're done using *aAttrCount as a c-style array index (ie starting // at 0). update it to reflect the number of elements now in the array // *aAttrCount += 1; } // free the position pointer, if necessary // if (position) { ldap_ber_free(position, 0); } return NS_OK; } // readonly attribute wstring dn; NS_IMETHODIMP nsLDAPMessage::GetDn(nsACString& aDn) { char *rawDn = ldap_get_dn(mConnectionHandle, mMsgHandle); if (!rawDn) { int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); switch (lderrno) { case LDAP_DECODING_ERROR: NS_WARNING("nsLDAPMessage::GetDn(): ldap decoding error"); return NS_ERROR_LDAP_DECODING_ERROR; case LDAP_PARAM_ERROR: default: NS_ERROR("nsLDAPMessage::GetDn(): internal error"); return NS_ERROR_UNEXPECTED; } } MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPMessage::GetDn(): dn = '%s'", rawDn)); aDn.Assign(rawDn); ldap_memfree(rawDn); return NS_OK; } // wrapper for ldap_get_values() // NS_IMETHODIMP nsLDAPMessage::GetValues(const char *aAttr, uint32_t *aCount, char16_t ***aValues) { char **values; #if defined(DEBUG) // We only want this being logged for debug builds so as not to affect performance too much. MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPMessage::GetValues(): called with aAttr = '%s'", aAttr)); #endif values = ldap_get_values(mConnectionHandle, mMsgHandle, aAttr); // bail out if there was a problem // if (!values) { int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); if ( lderrno == LDAP_DECODING_ERROR ) { // this may not be an error; it could just be that the // caller has asked for an attribute that doesn't exist. // MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, ("nsLDAPMessage::GetValues(): ldap_get_values returned " "LDAP_DECODING_ERROR")); return NS_ERROR_LDAP_DECODING_ERROR; } else if ( lderrno == LDAP_PARAM_ERROR ) { NS_ERROR("nsLDAPMessage::GetValues(): internal error: 1"); return NS_ERROR_UNEXPECTED; } else { NS_ERROR("nsLDAPMessage::GetValues(): internal error: 2"); return NS_ERROR_UNEXPECTED; } } // count the values // uint32_t numVals = ldap_count_values(values); // create an array of the appropriate size // *aValues = static_cast(moz_xmalloc(numVals * sizeof(char16_t *))); if (!*aValues) { ldap_value_free(values); return NS_ERROR_OUT_OF_MEMORY; } // clone the array (except for the trailing NULL entry) using the // shared allocator for XPCOM correctness // uint32_t i; for ( i = 0 ; i < numVals ; i++ ) { nsDependentCString sValue(values[i]); if (IsUTF8(sValue)) (*aValues)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(sValue)); else (*aValues)[i] = ToNewUnicode(NS_ConvertASCIItoUTF16(sValue)); if ( ! (*aValues)[i] ) { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues); ldap_value_free(values); return NS_ERROR_OUT_OF_MEMORY; } } // now free our value array since we already cloned the values array // to the 'aValues' results array. ldap_value_free(values); *aCount = numVals; return NS_OK; } // wrapper for get_values_len // NS_IMETHODIMP nsLDAPMessage::GetBinaryValues(const char *aAttr, uint32_t *aCount, nsILDAPBERValue ***aValues) { struct berval **values; #if defined(DEBUG) // We only want this being logged for debug builds so as not to affect performance too much. MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPMessage::GetBinaryValues(): called with aAttr = '%s'", aAttr)); #endif values = ldap_get_values_len(mConnectionHandle, mMsgHandle, aAttr); // bail out if there was a problem // if (!values) { int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); if ( lderrno == LDAP_DECODING_ERROR ) { // this may not be an error; it could just be that the // caller has asked for an attribute that doesn't exist. // MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, ("nsLDAPMessage::GetBinaryValues(): ldap_get_values " "returned LDAP_DECODING_ERROR")); return NS_ERROR_LDAP_DECODING_ERROR; } else if ( lderrno == LDAP_PARAM_ERROR ) { NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 1"); return NS_ERROR_UNEXPECTED; } else { NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 2"); return NS_ERROR_UNEXPECTED; } } // count the values // uint32_t numVals = ldap_count_values_len(values); // create the out array // *aValues = static_cast(moz_xmalloc(numVals * sizeof(nsILDAPBERValue))); if (!aValues) { ldap_value_free_len(values); return NS_ERROR_OUT_OF_MEMORY; } // clone the array (except for the trailing NULL entry) using the // shared allocator for XPCOM correctness // uint32_t i; nsresult rv; for ( i = 0 ; i < numVals ; i++ ) { // create an nsBERValue object // nsCOMPtr berValue = new nsLDAPBERValue(); if (!berValue) { NS_ERROR("nsLDAPMessage::GetBinaryValues(): out of memory" " creating nsLDAPBERValue object"); NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues); ldap_value_free_len(values); return NS_ERROR_OUT_OF_MEMORY; } // copy the value from the struct into the nsBERValue // rv = berValue->Set(values[i]->bv_len, reinterpret_cast(values[i]->bv_val)); if (NS_FAILED(rv)) { NS_ERROR("nsLDAPMessage::GetBinaryValues(): error setting" " nsBERValue"); ldap_value_free_len(values); return rv == NS_ERROR_OUT_OF_MEMORY ? rv : NS_ERROR_UNEXPECTED; } // put the nsIBERValue object into the out array // NS_ADDREF( (*aValues)[i] = berValue.get() ); } *aCount = numVals; ldap_value_free_len(values); return NS_OK; } // readonly attribute nsILDAPOperation operation; NS_IMETHODIMP nsLDAPMessage::GetOperation(nsILDAPOperation **_retval) { if (!_retval) { NS_ERROR("nsLDAPMessage::GetOperation: null pointer "); return NS_ERROR_NULL_POINTER; } NS_IF_ADDREF(*_retval = mOperation); return NS_OK; } NS_IMETHODIMP nsLDAPMessage::ToUnicode(char16_t* *aString) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsLDAPMessage::GetErrorMessage(nsACString & aErrorMessage) { aErrorMessage.Assign(mErrorMessage); return NS_OK; } NS_IMETHODIMP nsLDAPMessage::GetMatchedDn(nsACString & aMatchedDn) { aMatchedDn.Assign(mMatchedDn); return NS_OK; }