diff options
Diffstat (limited to 'ldap/xpcom/src/nsLDAPMessage.cpp')
-rw-r--r-- | ldap/xpcom/src/nsLDAPMessage.cpp | 649 |
1 files changed, 649 insertions, 0 deletions
diff --git a/ldap/xpcom/src/nsLDAPMessage.cpp b/ldap/xpcom/src/nsLDAPMessage.cpp new file mode 100644 index 0000000000..782fa047ff --- /dev/null +++ b/ldap/xpcom/src/nsLDAPMessage.cpp @@ -0,0 +1,649 @@ +/* -*- 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<nsLDAPConnection *>(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<char **>(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<char16_t **>(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<nsILDAPBERValue **>(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<nsILDAPBERValue> 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<uint8_t *>(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; +} |