Files
Mozilla/mozilla/directory/xpcom/base/src/nsLDAPMessage.cpp
bugzilla%standard8.demon.co.uk bd03c525c4 Bug 136773 Investigate enabling ldap:5 logging on optimized builds. r/sr=dmose,a=mscott
git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@188350 18797224-902f-48f8-a5cc-f745e15eee43
2006-01-27 17:50:03 +00:00

684 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the mozilla.org LDAP XPCOM SDK.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dan Mosedale <dmose@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsLDAPInternal.h"
#include "nsLDAPMessage.h"
#include "nspr.h"
#include "nsDebug.h"
#include "nsCRT.h"
#include "nsLDAPConnection.h"
#include "nsReadableUtils.h"
#include "nsISupportsUtils.h"
#include "nsLDAPBERValue.h"
NS_IMPL_THREADSAFE_ADDREF(nsLDAPMessage)
NS_IMPL_THREADSAFE_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_GETTER1(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)
PR_LOG(gLDAPLogModule, PR_LOG_WARNING,
("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() "
"timed out\n"));
break;
default:
// other failure
PR_LOG(gLDAPLogModule, PR_LOG_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 = NS_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(PRInt32 *aErrorCode)
{
if (!aErrorCode) {
return NS_ERROR_ILLEGAL_VALUE;
}
*aErrorCode = mErrorCode;
return NS_OK;
}
NS_IMETHODIMP
nsLDAPMessage::GetType(PRInt32 *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(PRInt32 aLderrno, PRUint32 *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(PRUint32 *aAttrCount, char** *aAttributes)
{
return IterateAttributes(aAttrCount, aAttributes, PR_TRUE);
}
// if getP is PR_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 PR_FALSE, just fill in *attrCount and return
//
nsresult
nsLDAPMessage::IterateAttributes(PRUint32 *aAttrCount, char** *aAttributes,
PRBool 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, PR_FALSE);
if (NS_FAILED(rv))
return rv;
// create an array of the appropriate size
//
*aAttributes = NS_STATIC_CAST(char **,
nsMemory::Alloc(*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] = nsCRT::strdup(attr);
if (!(*aAttributes)[0]) {
ldap_memfree(attr);
nsMemory::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
//
PRInt32 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] = nsCRT::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) {
PRInt32 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;
}
}
PR_LOG(gLDAPLogModule, PR_LOG_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, PRUint32 *aCount,
PRUnichar ***aValues)
{
char **values;
#if defined(DEBUG)
// We only want this being logged for debug builds so as not to affect performance too much.
PR_LOG(gLDAPLogModule, PR_LOG_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) {
PRInt32 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.
//
PR_LOG(gLDAPLogModule, PR_LOG_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
//
PRUint32 numVals = ldap_count_values(values);
// create an array of the appropriate size
//
*aValues = NS_STATIC_CAST(PRUnichar **,
nsMemory::Alloc(numVals * sizeof(PRUnichar *)));
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
//
PRUint32 i;
for ( i = 0 ; i < numVals ; i++ ) {
nsDependentCString sValue(values[i]);
if (IsUTF8(sValue))
(*aValues)[i] = UTF8ToNewUnicode(sValue);
else
(*aValues)[i] = ToNewUnicode(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, PRUint32 *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.
PR_LOG(gLDAPLogModule, PR_LOG_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) {
PRInt32 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.
//
PR_LOG(gLDAPLogModule, PR_LOG_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
//
PRUint32 numVals = ldap_count_values_len(values);
// create the out array
//
*aValues =
NS_STATIC_CAST(nsILDAPBERValue **,
nsMemory::Alloc(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
//
PRUint32 i;
nsresult rv;
nsCOMPtr<nsILDAPBERValue> berValue;
for ( i = 0 ; i < numVals ; i++ ) {
// create an nsBERValue object
//
NS_NEWXPCOM(berValue, 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,
NS_REINTERPRET_CAST(PRUint8 *, 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(PRUnichar* *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;
}