summaryrefslogtreecommitdiffstats
path: root/ldap/xpcom
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/xpcom')
-rw-r--r--ldap/xpcom/README.txt24
-rw-r--r--ldap/xpcom/TODO.txt155
-rw-r--r--ldap/xpcom/moz.build9
-rw-r--r--ldap/xpcom/public/moz.build27
-rw-r--r--ldap/xpcom/public/nsILDAPBERElement.idl122
-rw-r--r--ldap/xpcom/public/nsILDAPBERValue.idl44
-rw-r--r--ldap/xpcom/public/nsILDAPConnection.idl77
-rw-r--r--ldap/xpcom/public/nsILDAPControl.idl45
-rw-r--r--ldap/xpcom/public/nsILDAPErrors.idl447
-rw-r--r--ldap/xpcom/public/nsILDAPMessage.idl170
-rw-r--r--ldap/xpcom/public/nsILDAPMessageListener.idl40
-rw-r--r--ldap/xpcom/public/nsILDAPModification.idl58
-rw-r--r--ldap/xpcom/public/nsILDAPOperation.idl275
-rw-r--r--ldap/xpcom/public/nsILDAPServer.idl86
-rw-r--r--ldap/xpcom/public/nsILDAPService.idl197
-rw-r--r--ldap/xpcom/public/nsILDAPSyncQuery.idl27
-rw-r--r--ldap/xpcom/public/nsILDAPURL.idl132
-rw-r--r--ldap/xpcom/src/ldapComponents.manifest5
-rw-r--r--ldap/xpcom/src/moz.build53
-rw-r--r--ldap/xpcom/src/nsLDAPBERElement.cpp117
-rw-r--r--ldap/xpcom/src/nsLDAPBERElement.h30
-rw-r--r--ldap/xpcom/src/nsLDAPBERValue.cpp106
-rw-r--r--ldap/xpcom/src/nsLDAPBERValue.h40
-rw-r--r--ldap/xpcom/src/nsLDAPConnection.cpp737
-rw-r--r--ldap/xpcom/src/nsLDAPConnection.h139
-rw-r--r--ldap/xpcom/src/nsLDAPControl.cpp122
-rw-r--r--ldap/xpcom/src/nsLDAPControl.h42
-rw-r--r--ldap/xpcom/src/nsLDAPInternal.h11
-rw-r--r--ldap/xpcom/src/nsLDAPMessage.cpp649
-rw-r--r--ldap/xpcom/src/nsLDAPMessage.h66
-rw-r--r--ldap/xpcom/src/nsLDAPModification.cpp158
-rw-r--r--ldap/xpcom/src/nsLDAPModification.h42
-rw-r--r--ldap/xpcom/src/nsLDAPOperation.cpp975
-rw-r--r--ldap/xpcom/src/nsLDAPOperation.h104
-rw-r--r--ldap/xpcom/src/nsLDAPProtocolHandler.js62
-rw-r--r--ldap/xpcom/src/nsLDAPProtocolModule.cpp147
-rw-r--r--ldap/xpcom/src/nsLDAPSecurityGlue.cpp342
-rw-r--r--ldap/xpcom/src/nsLDAPServer.cpp133
-rw-r--r--ldap/xpcom/src/nsLDAPServer.h39
-rw-r--r--ldap/xpcom/src/nsLDAPService.cpp985
-rw-r--r--ldap/xpcom/src/nsLDAPService.h116
-rw-r--r--ldap/xpcom/src/nsLDAPSyncQuery.cpp386
-rw-r--r--ldap/xpcom/src/nsLDAPSyncQuery.h58
-rw-r--r--ldap/xpcom/src/nsLDAPURL.cpp750
-rw-r--r--ldap/xpcom/src/nsLDAPURL.h61
-rw-r--r--ldap/xpcom/src/nsLDAPUtils.h147
46 files changed, 8557 insertions, 0 deletions
diff --git a/ldap/xpcom/README.txt b/ldap/xpcom/README.txt
new file mode 100644
index 000000000..d621b14fc
--- /dev/null
+++ b/ldap/xpcom/README.txt
@@ -0,0 +1,24 @@
+This is the home for XPCOM modules that implement LDAP functionality.
+
+What's Here
+-----------
+base/
+ Implements a wrapper around the LDAP C SDK, as well as support
+ for ldap: URLs in the browser. Written entirely in C++;
+ theoretically only depends on necko, xpcom, nspr, and the LDAP
+ C SDK.
+
+datasource/
+ An RDF datasource, written in Javascript.
+
+tests/
+ Some basic tests to help ensure that things don't break as
+ development proceeds. Currently, there is only some stuff for
+ testing the datasource.
+
+
+Building
+--------
+See <http://www.mozilla.org/directory/xpcom.html>.
+
+Dan Mosedale <dmose@mozilla.org>
diff --git a/ldap/xpcom/TODO.txt b/ldap/xpcom/TODO.txt
new file mode 100644
index 000000000..ff70c2bcd
--- /dev/null
+++ b/ldap/xpcom/TODO.txt
@@ -0,0 +1,155 @@
+housecleaning for first 0.x release
+-----------------------------------
+* error handling: sort out what should be NS_ASSERTIONs vs. normal
+ error conditions (eg network & data errors), especially in nsLDAPChannel.
+ also shouldn't be casting results of nsILDAPConnection::GetErrorString to
+ void in ldapSearch. (ideally this would use xpidl nsresult decls
+ (bug 13423), but that's not done yet). This also requires some
+ threadsafety work related to the data and interfaces touched by
+ nsLDAPChannel::Cancel, since these are used in the connection
+ callbacks, and we also need to be able to cancel from the connection
+ callbacks themselves.
+
+* audit for and fix leaks / bloat
+
+* destroy connection & thread when done with it; deal with timeouts
+ (blocked on LDAP C SDK 4.1, in the hopes that the NSPR
+ binding of the IO functions will provide us with some way to pop out
+ of the select() that's called under ldap_result(). It may not be worth
+ implementing this until nsLDAPService is resurrected. If this
+ doesn't work, we may have to give nsLDAPConnection an event queue &
+ loop of its own, instead of using the LDAP C SDK as a vector to get
+ events over there.
+
+items blocked waiting for other work
+------------------------------------
+* go through the various options that can be used with the SDK function
+ ldap_set_options() and decide if and where the various functions should
+ be used. This will include memory allocator settings, and possibly
+ threadsafety callbacks (if necessary). (blocked waiting on LDAP C
+ SDK 4.1 to land, since it contains NSPR bindings for some of this).
+
+* searches that return lots of entries to the nsLDAPChannel
+ (eg (sn=Smith) at netcenter) mostly stall out the UI. moving most of the
+ callback work off the UI thread to the LDAP connection thread didn't
+ help; so this seems to be lossage in the event system itself (bug 50104).
+
+* deal with race condition that happens if data comes back after
+ nsLDAPChannel::Cancel is called. AsyncStreamListener expects
+ GetStatus to return OK, and asserts when it doesn't. (blocked waiting
+ on insight from Warren about nsSocketTransport's use of mCancelStatus).
+
+* ensure that multiple calls to Cancel don't do the wrong thing
+
+* move the ldap_unbind() call out of the nsLDAPConnection destructor,
+ since nsLDAPConnection will soon go back to being callable from the
+ UI thread, and ldap_unbind() is actually synchronous. additionally,
+ arrange for the connection thread to shutdown. (waiting for
+ nsILDAPService to be implemented on the theory that nsILDAPService
+ might have it's own thread and it would be reasonable to call
+ ldap_unbind() from that thread).
+
+* currently nsILDAPOperation::SimpleBind is called as though it were
+ asynchronous (ie from the UI thread), which it mostly is -- the call
+ to connect() can stall. We could use the ASYNC_CONNECT flag, but it
+ seems to do weird stuff on Linux, and mcs says it isn't well tested
+ anyway. At the moment, my feeling is that fixing ASYNC_CONNECT is
+ probably the right thing to do, but the bind could actually be
+ pushed from nsILDAPOperation into nsILDAPConnection and then called
+ after the thread for the connection is spun up (waiting on help from
+ the LDAP SDK folks: bug 50074).
+
+misc
+----
+* verify that ldap_parse_result() and ldap_get_ldaperrno() aren't
+ required to be called from the same thread as ldap_result() and
+ before the thread-specific ldaperrno has a chance to change. Note:
+ now that ldap_parse_result is only called inside the initializer
+ nsLDAPMessage::Init(), this is probably no longer an issue there at
+ least. Need to confirm.
+
+* replace instances of (obsolete) |static NS_DEFINE_IID| variables
+ with direct calls to NS_GET_IID in the code.
+
+* investigate use of DNS in LDAP sdk. I think sync functions used in the
+ wrong places (eg they end up getting called from Mozilla on the UI thread)?
+
+* audit for and implement any appropriate NOT_IMPLEMENTED functions
+
+* re-read the IDL for interfaces implemented by nsLDAPChannel.cpp make sure
+ all interfaces are implemented correctly
+
+* implement progress info interfaces, if possible (% done)
+
+* investigate the MOZILLA_CLIENT define as used by the SDK. eg do we still
+ want the reference to "xp_sort.h"?
+
+* i18n / l10n (any text strings)
+
+* cachability of nsILDAPChannel content: browser cache? ldap_memcache? both?
+
+* use a rebind_proc?
+
+* HTMLize nsLDAPChannel output for linkification
+* the LDAP SDK returns UTF8, but I think nsILDAPChannel is handing it
+ off to callers as ASCII. How does this all relate to the stated
+ UCS2 policy in Mozilla?
+
+* all attributes are assumed to be strings right now. This probably
+ needs to change: assume all attributes are binary, use some
+ heuristic to figure out if they're a string. I wonder how
+ ldapsearch does this.
+
+* grep for XXXs and fix the issues
+
+rdf datasource
+--------------
+
+* revamp nsILDAPService (currently unused code) to manage LDAP
+ connections and allow for sharing connections between browser
+ clients. I think this should obviate the need to hold onto the
+ connection with a delegate factory.
+
+* non-anonymous binding (ie nsLDAPURL supports x-bind-name -- I
+ suspect this may come with the LDAP C SDK version after 4.1,
+ though.)
+
+testing
+-------
+* see how the browser copes when it does such a big search that the server
+ only returns partial results and an error.
+
+perf
+----
+* rather than having one thread per nsILDAPConnection, have multiple
+ nsILDAPConnections share the same thread. possibly pre-spin up a
+ thread-pool to handle this?
+
+* nsLDAPService: assuming that we do the above and start using
+ nsLDAPService again, need to implement shutdown for nsLDAPService
+ (probably analogous to the way nsSocketTransportService shuts down.
+ but how does nsIShutdownListener fit in here? and CanUnload?)
+
+* remove any unnecessary thread-safety code (NS_IMPL_THREADSAFE) to avoid
+ locking overhead. need to figure out if everything must be
+ threadsafe or not.
+
+later
+-----
+* rationalize the logging strategy. right now there is a mix of
+ PR_LOG/NS{WARNING|ERROR} and nsIConsoleService and non-logging,
+ as I have been vacillating on how much logging it's really important to have
+ (part of my thought process about the XPCOM-wrapper being part of
+ Mozilla-as-platform)
+* get rid of inappropriate use of nsVoidKey by implementing an nsPRInt32Key
+* handle referrals
+* failover
+* addressbook/mail UI glue
+* PSM certs from directory glue(?)
+* secure (& proxied/socksified?) ldap
+* make the LDAP C SDK autoconf glue not be a shim and not require
+ nsprpub build infrastructure. requires work with the LDAP C SDK
+ owners, and shouldn't this shouldn't happen until after the most
+ current ldap SDK code lands in mozilla.org anyway.
+* figure out our strategy for LDAPv2 vs. LDAPv3. right now, the code just
+ uses the C SDK's default of always advertising itself as LDAPv2.
diff --git a/ldap/xpcom/moz.build b/ldap/xpcom/moz.build
new file mode 100644
index 000000000..27dcb8746
--- /dev/null
+++ b/ldap/xpcom/moz.build
@@ -0,0 +1,9 @@
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ 'public',
+ 'src',
+]
diff --git a/ldap/xpcom/public/moz.build b/ldap/xpcom/public/moz.build
new file mode 100644
index 000000000..9ff6b21c6
--- /dev/null
+++ b/ldap/xpcom/public/moz.build
@@ -0,0 +1,27 @@
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsILDAPBERElement.idl',
+ 'nsILDAPBERValue.idl',
+ 'nsILDAPConnection.idl',
+ 'nsILDAPControl.idl',
+ 'nsILDAPErrors.idl',
+ 'nsILDAPMessage.idl',
+ 'nsILDAPMessageListener.idl',
+ 'nsILDAPModification.idl',
+ 'nsILDAPOperation.idl',
+ 'nsILDAPServer.idl',
+ 'nsILDAPService.idl',
+ 'nsILDAPURL.idl',
+]
+
+if CONFIG['MOZ_PREF_EXTENSIONS']:
+ XPIDL_SOURCES += [
+ 'nsILDAPSyncQuery.idl',
+ ]
+
+XPIDL_MODULE = 'mozldap'
+
diff --git a/ldap/xpcom/public/nsILDAPBERElement.idl b/ldap/xpcom/public/nsILDAPBERElement.idl
new file mode 100644
index 000000000..24a662782
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPBERElement.idl
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsISupports.idl"
+
+interface nsILDAPBERValue;
+
+
+/**
+ * nsILDAPBERElement is a wrapper interface for a C-SDK BerElement object.
+ * Typically, this is used as an intermediate object to aid in the manual
+ * construction of a BER value. Once the construction is completed by calling
+ * methods on this object, an nsILDAPBERValue can be retrieved from the
+ * asValue attribute on this interface.
+ *
+ * <http://www.mozilla.org/directory/ietf-docs/draft-ietf-ldapext-ldap-c-api-05.txt>
+ * contains some documentation that mostly (but not exactly) matches
+ * the code that this wraps in section 17.
+ */
+
+[scriptable, uuid(409f5b31-c062-4d11-a35b-0a09e7967bf2)]
+interface nsILDAPBERElement : nsISupports
+{
+ /**
+ * Initialize this object. Must be called before calling any other method
+ * on this interface.
+ *
+ * @param aValue value to preinitialize with; 0 for a new empty object
+ *
+ * @exception NS_ERROR_NOT_IMPLEMENTED preinitialization is currently
+ * not implemented
+ * @exception NS_ERROR_OUT_OF_MEMORY unable to allocate the internal
+ * BerElement
+ */
+ void init(in nsILDAPBERValue aValue);
+
+ /**
+ * Most TAG_* constants can be used in the construction or passing in of
+ * values to the aTag arguments to most of the methods in this interface.
+ */
+
+ /**
+ * When returned from a parsing method, 0xffffffff is referred to
+ * has the parse-error semantic (ie TAG_LBER_ERROR); when passing it to
+ * a construction method, it is used to mean "pick the default tag for
+ * this type" (ie TAG_LBER_DEFAULT).
+ */
+ const unsigned long TAG_LBER_ERROR = 0xffffffff;
+ const unsigned long TAG_LBER_DEFAULT = 0xffffffff;
+ const unsigned long TAG_LBER_END_OF_SEQORSET = 0xfffffffe;
+
+ /**
+ * BER encoding types and masks
+ */
+ const unsigned long TAG_LBER_PRIMITIVE = 0x00;
+
+ /**
+ * The following two tags are carried over from the LDAP C SDK; their
+ * exact purpose there is not well documented. They both have
+ * the same value there as well.
+ */
+ const unsigned long TAG_LBER_CONSTRUCTED = 0x20;
+ const unsigned long TAG_LBER_ENCODING_MASK = 0x20;
+
+ const unsigned long TAG_LBER_BIG_TAG_MASK = 0x1f;
+ const unsigned long TAG_LBER_MORE_TAG_MASK = 0x80;
+
+ /**
+ * general BER types we know about
+ */
+ const unsigned long TAG_LBER_BOOLEAN = 0x01;
+ const unsigned long TAG_LBER_INTEGER = 0x02;
+ const unsigned long TAG_LBER_BITSTRING = 0x03;
+ const unsigned long TAG_LBER_OCTETSTRING = 0x04;
+ const unsigned long TAG_LBER_NULL = 0x05;
+ const unsigned long TAG_LBER_ENUMERATED = 0x0a;
+ const unsigned long TAG_LBER_SEQUENCE = 0x30;
+ const unsigned long TAG_LBER_SET = 0x31;
+
+ /**
+ * Write a string to this element.
+ *
+ * @param aString string to write
+ * @param aTag tag for this string (if TAG_LBER_DEFAULT is used,
+ * TAG_LBER_OCTETSTRING will be written).
+ *
+ * @return number of bytes written
+ *
+ * @exception NS_ERROR_FAILUE C-SDK returned error
+ */
+ unsigned long putString(in AUTF8String aString, in unsigned long aTag);
+
+ /**
+ * Start a set. Sets may be nested.
+ *
+ * @param aTag tag for this set (if TAG_LBER_DEFAULT is used,
+ * TAG_LBER_SET will be written).
+ *
+ * @exception NS_ERROR_FAILUE C-SDK returned an error
+ */
+ void startSet(in unsigned long aTag);
+
+ /**
+ * Cause the entire set started by the last startSet() call to be written.
+ *
+ * @exception NS_ERROR_FAILUE C-SDK returned an error
+ *
+ * @return number of bytes written
+ */
+ unsigned long putSet();
+
+ /**
+ * an nsILDAPBERValue version of this element. Calls ber_flatten() under
+ * the hood.
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY
+ */
+ readonly attribute nsILDAPBERValue asValue;
+};
diff --git a/ldap/xpcom/public/nsILDAPBERValue.idl b/ldap/xpcom/public/nsILDAPBERValue.idl
new file mode 100644
index 000000000..da918d639
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPBERValue.idl
@@ -0,0 +1,44 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * Representation of a BER value as an interface containing an array of
+ * bytes. Someday this should perhaps be obsoleted by a better, more
+ * generalized version of nsIByteBuffer, but that's currently not even
+ * scriptable (see bug 125596).
+ */
+[scriptable, uuid(c817c5fe-1dd1-11b2-a10b-ae9885762ea9)]
+interface nsILDAPBERValue : nsISupports
+{
+ /**
+ * Set the BER value from an array of bytes (copies).
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to
+ */
+ void set(in unsigned long aCount,
+ [array, size_is(aCount)] in octet aValue);
+
+ /**
+ * Set the BER value from a UTF8 string (copies).
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to
+ */
+ void setFromUTF8(in AUTF8String aValue);
+
+ /**
+ * Get the BER value as an array of bytes. Note that if this value is
+ * zero-length, aCount and aRetVal will both be 0. This means that
+ * (in C++ anyway) the caller MUST test either aCount or aRetval before
+ * dereferencing aRetVal.
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to
+ */
+ void get(out unsigned long aCount,
+ [retval, array, size_is(aCount)] out octet aRetVal);
+};
+
diff --git a/ldap/xpcom/public/nsILDAPConnection.idl b/ldap/xpcom/public/nsILDAPConnection.idl
new file mode 100644
index 000000000..63b367363
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPConnection.idl
@@ -0,0 +1,77 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsILDAPOperation;
+interface nsILDAPMessageListener;
+interface nsILDAPURL;
+
+%{C++
+#define NS_LDAPCONNECTION_CONTRACTID "@mozilla.org/network/ldap-connection;1"
+%}
+
+[scriptable, uuid(360c1ff7-15e3-4ffe-b4b8-0eda72ebc096)]
+interface nsILDAPConnection : nsISupports
+{
+ /**
+ * the string version of lderrno
+ */
+ readonly attribute wstring errorString;
+
+ /**
+ * DN to bind as. use the init() method to set this.
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY
+ */
+ readonly attribute AUTF8String bindName;
+
+ /**
+ * private parameter (anything caller desires)
+ */
+ attribute nsISupports closure;
+
+ /**
+ * Set up the connection. Note that init() must be called on a thread
+ * that already has an nsIEventQueue.
+ *
+ * @param aUrl A URL for the ldap server. The host, port and
+ * ssl connection type will be extracted from this
+ * @param aBindName DN to bind as
+ * @param aMessageListener Callback for DNS resolution completion
+ * @param aClosure private parameter (anything caller desires)
+ * @param aVersion LDAP version to use (currently VERSION2 or
+ * VERSION3)
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer or invalid version
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_OFFLINE we are in off-line mode
+ * @exception NS_ERROR_FAILURE
+ * @exception NS_ERROR_UNEXPECTED internal error
+ */
+ void init(in nsILDAPURL aUrl,
+ in AUTF8String aBindName,
+ in nsILDAPMessageListener aMessageListener,
+ in nsISupports aClosure, in unsigned long aVersion);
+
+ const unsigned long VERSION2 = 2;
+ const unsigned long VERSION3 = 3;
+
+ /**
+ * Get information about the last error that occured on this connection.
+ *
+ * @param matched if the server is returning LDAP_NO_SUCH_OBJECT,
+ * LDAP_ALIAS_PROBLEM, LDAP_INVALID_DN_SYNTAX,
+ * or LDAP_ALIAS_DEREF_PROBLEM, this will contain
+ * the portion of DN that matches the entry that is
+ * closest to the requested entry
+ *
+ * @param s additional error information from the server
+ *
+ * @return the error code, as defined in nsILDAPErrors.idl
+ */
+ long getLdErrno(out AUTF8String matched, out AUTF8String s);
+};
diff --git a/ldap/xpcom/public/nsILDAPControl.idl b/ldap/xpcom/public/nsILDAPControl.idl
new file mode 100644
index 000000000..89b87f850
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPControl.idl
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsISupports.idl"
+
+interface nsILDAPBERValue;
+
+/**
+ * XPCOM representation of the C SDK LDAPControl structure.
+ */
+[scriptable, uuid(3a7ceb8e-482a-4a4f-9aa4-26b9a69a3595)]
+interface nsILDAPControl : nsISupports
+{
+ /**
+ * Control type, represented as a string.
+ *
+ * @exceptions none
+ */
+ attribute ACString oid;
+
+ /**
+ * The data associated with a control, if any. To specify that no data
+ * is to be associated with the control, don't set this at all (which
+ * is equivalent to setting it to null).
+ *
+ * @note Specifying a zero-length value is not currently supported. At some
+ * date, setting this to an nsILDAPBERValue which has not had any of the
+ * set methods called will be the appropriate way to do that.
+ *
+ * @exceptions none
+ */
+ attribute nsILDAPBERValue value;
+
+ /**
+ * Should the client or server abort if the control is not understood?
+ * Should be set to false for server controls used in abandon and unbind
+ * operations, since those have no server response.
+ *
+ * @exceptions none
+ */
+ attribute boolean isCritical;
+};
diff --git a/ldap/xpcom/public/nsILDAPErrors.idl b/ldap/xpcom/public/nsILDAPErrors.idl
new file mode 100644
index 000000000..f85f75d79
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPErrors.idl
@@ -0,0 +1,447 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * Error codes used in the LDAP XPCOM SDK.
+ *
+ * Taken from the Mozilla C SDK's ldap.h include file, these should be
+ * the same as those specified in the draft-ietf-ldapext-ldap-c-api-04.txt
+ * Internet Draft.
+ *
+ * The only good documentation I'm aware of for these error codes is
+ * at <http://docs.iplanet.com/docs/manuals/directory.html#SDKC>.
+ * Unfortunately, this does not currently seem to be available under any
+ * open source license, so I can't include that documentation here as
+ * doxygen comments.
+ *
+ */
+[scriptable, uuid(f9ac10fa-1dd1-11b2-9798-8d5cbda95d74)]
+interface nsILDAPErrors : nsISupports
+{
+
+ const long SUCCESS = 0x00;
+
+ const long OPERATIONS_ERROR = 0x01;
+
+ const long PROTOCOL_ERROR = 0x02;
+
+ const long TIMELIMIT_EXCEEDED = 0x03;
+
+ const long SIZELIMIT_EXCEEDED = 0x04;
+
+ const long COMPARE_FALSE = 0x05;
+
+ const long COMPARE_TRUE = 0x06;
+
+ const long STRONG_AUTH_NOT_SUPPORTED = 0x07;
+
+ const long STRONG_AUTH_REQUIRED = 0x08;
+
+
+ /**
+ * UMich LDAPv2 extension
+ */
+ const long PARTIAL_RESULTS = 0x09;
+
+ /**
+ * new in LDAPv3
+ */
+ const long REFERRAL = 0x0a;
+
+ /**
+ * new in LDAPv3
+ */
+ const long ADMINLIMIT_EXCEEDED = 0x0b;
+
+ /**
+ * new in LDAPv3
+ */
+ const long UNAVAILABLE_CRITICAL_EXTENSION = 0x0c;
+
+ /**
+ * new in LDAPv3
+ */
+ const long CONFIDENTIALITY_REQUIRED = 0x0d;
+
+ /**
+ * new in LDAPv3
+ */
+ const long SASL_BIND_IN_PROGRESS = 0x0e;
+
+ const long NO_SUCH_ATTRIBUTE = 0x10;
+
+ const long UNDEFINED_TYPE = 0x11;
+
+ const long INAPPROPRIATE_MATCHING = 0x12;
+
+ const long CONSTRAINT_VIOLATION = 0x13;
+
+ const long TYPE_OR_VALUE_EXISTS = 0x14;
+
+ const long INVALID_SYNTAX = 0x15;
+
+ const long NO_SUCH_OBJECT = 0x20;
+
+ const long ALIAS_PROBLEM = 0x21;
+
+ const long INVALID_DN_SYNTAX = 0x22;
+
+ /**
+ * not used in LDAPv3
+ */
+ const long IS_LEAF = 0x23;
+
+ const long ALIAS_DEREF_PROBLEM = 0x24;
+
+ const long INAPPROPRIATE_AUTH = 0x30;
+
+ const long INVALID_CREDENTIALS = 0x31;
+
+ const long INSUFFICIENT_ACCESS = 0x32;
+
+ const long BUSY = 0x33;
+
+ const long UNAVAILABLE = 0x34;
+
+ const long UNWILLING_TO_PERFORM = 0x35;
+
+ const long LOOP_DETECT = 0x36;
+
+ /**
+ * server side sort extension
+ */
+ const long SORT_CONTROL_MISSING = 0x3C;
+
+ /**
+ * VLV extension
+ */
+ const long INDEX_RANGE_ERROR = 0x3D;
+
+ const long NAMING_VIOLATION = 0x40;
+
+ const long OBJECT_CLASS_VIOLATION = 0x41;
+
+ const long NOT_ALLOWED_ON_NONLEAF = 0x42;
+
+ const long NOT_ALLOWED_ON_RDN = 0x43;
+
+ const long ALREADY_EXISTS = 0x44;
+
+ const long NO_OBJECT_CLASS_MODS = 0x45;
+
+ /**
+ * reserved CLDAP
+ */
+ const long RESULTS_TOO_LARGE = 0x46;
+
+ /**
+ * new in LDAPv3
+ */
+ const long AFFECTS_MULTIPLE_DSAS = 0x47;
+
+ const long OTHER = 0x50;
+
+ const long SERVER_DOWN = 0x51;
+
+ const long LOCAL_ERROR = 0x52;
+
+ const long ENCODING_ERROR = 0x53;
+
+ const long DECODING_ERROR = 0x54;
+
+ const long TIMEOUT = 0x55;
+
+ const long AUTH_UNKNOWN = 0x56;
+
+ const long FILTER_ERROR = 0x57;
+
+ const long USER_CANCELLED = 0x58;
+
+ const long PARAM_ERROR = 0x59;
+
+ const long NO_MEMORY = 0x5a;
+
+ const long CONNECT_ERROR = 0x5b;
+
+ /**
+ * new in LDAPv3
+ */
+ const long NOT_SUPPORTED = 0x5c;
+
+ /**
+ * new in LDAPv3
+ */
+ const long CONTROL_NOT_FOUND = 0x5d;
+
+ /**
+ * new in LDAPv3
+ */
+ const long NO_RESULTS_RETURNED = 0x5e;
+
+ /**
+ * new in LDAPv3
+ */
+ const long MORE_RESULTS_TO_RETURN = 0x5f;
+
+ /**
+ * new in LDAPv3
+ */
+ const long CLIENT_LOOP = 0x60;
+
+ /**
+ * new in LDAPv3
+ */
+ const long REFERRAL_LIMIT_EXCEEDED = 0x61;
+};
+
+/*
+ * Map these errors codes into the nsresult namespace in C++
+ */
+%{C++
+
+#define NS_ERROR_LDAP_OPERATIONS_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::OPERATIONS_ERROR)
+
+#define NS_ERROR_LDAP_PROTOCOL_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::PROTOCOL_ERROR)
+
+#define NS_ERROR_LDAP_TIMELIMIT_EXCEEDED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::TIMELIMIT_EXCEEDED)
+
+#define NS_ERROR_LDAP_SIZELIMIT_EXCEEDED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::SIZELIMIT_EXCEEDED)
+
+#define NS_ERROR_LDAP_COMPARE_FALSE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::COMPARE_FALSE)
+
+#define NS_ERROR_LDAP_COMPARE_TRUE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::COMPARE_TRUE)
+
+#define NS_ERROR_LDAP_STRONG_AUTH_NOT_SUPPORTED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::STRONG_AUTH_NOT_SUPPORTED)
+
+#define NS_ERROR_LDAP_STRONG_AUTH_REQUIRED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::STRONG_AUTH_REQUIRED)
+
+#define NS_ERROR_LDAP_PARTIAL_RESULTS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::PARTIAL_RESULTS)
+
+#define NS_ERROR_LDAP_REFERRAL \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::REFERRAL)
+
+#define NS_ERROR_LDAP_ADMINLIMIT_EXCEEDED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::ADMINLIMIT_EXCEEDED)
+
+#define NS_ERROR_LDAP_UNAVAILABLE_CRITICAL_EXTENSION \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::UNAVAILABLE_CRITICAL_EXTENSION)
+
+#define NS_ERROR_LDAP_CONFIDENTIALITY_REQUIRED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::CONFIDENTIALITY_REQUIRED)
+
+#define NS_ERROR_LDAP_SASL_BIND_IN_PROGRESS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::SASL_BIND_IN_PROGRESS)
+
+#define NS_ERROR_LDAP_NO_SUCH_ATTRIBUTE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NO_SUCH_ATTRIBUTE)
+
+#define NS_ERROR_LDAP_UNDEFINED_TYPE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::UNDEFINED_TYPE)
+
+#define NS_ERROR_LDAP_INAPPROPRIATE_MATCHING \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INAPPROPRIATE_MATCHING)
+
+#define NS_ERROR_LDAP_CONSTRAINT_VIOLATION \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::CONSTRAINT_VIOLATION)
+
+#define NS_ERROR_LDAP_TYPE_OR_VALUE_EXISTS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::TYPE_OR_VALUE_EXISTS)
+
+#define NS_ERROR_LDAP_INVALID_SYNTAX \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INVALID_SYNTAX)
+
+#define NS_ERROR_LDAP_NO_SUCH_OBJECT \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NO_SUCH_OBJECT)
+
+#define NS_ERROR_LDAP_ALIAS_PROBLEM \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::ALIAS_PROBLEM)
+
+#define NS_ERROR_LDAP_INVALID_DN_SYNTAX \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INVALID_DN_SYNTAX)
+
+#define NS_ERROR_LDAP_IS_LEAF \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::IS_LEAF)
+
+#define NS_ERROR_LDAP_ALIAS_DEREF_PROBLEM \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::ALIAS_DEREF_PROBLEM)
+
+#define NS_ERROR_LDAP_INAPPROPRIATE_AUTH \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INAPPROPRIATE_AUTH)
+
+#define NS_ERROR_LDAP_INVALID_CREDENTIALS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INVALID_CREDENTIALS)
+
+#define NS_ERROR_LDAP_INSUFFICIENT_ACCESS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INSUFFICIENT_ACCESS)
+
+#define NS_ERROR_LDAP_BUSY \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::BUSY)
+
+#define NS_ERROR_LDAP_UNAVAILABLE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::UNAVAILABLE)
+
+#define NS_ERROR_LDAP_UNWILLING_TO_PERFORM \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::UNWILLING_TO_PERFORM)
+
+#define NS_ERROR_LDAP_LOOP_DETECT \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::LOOP_DETECT)
+
+#define NS_ERROR_LDAP_SORT_CONTROL_MISSING \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::SORT_CONTROL_MISSING)
+
+#define NS_ERROR_LDAP_INDEX_RANGE_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::INDEX_RANGE_ERROR)
+
+#define NS_ERROR_LDAP_NAMING_VIOLATION \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NAMING_VIOLATION)
+
+#define NS_ERROR_LDAP_OBJECT_CLASS_VIOLATION \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::OBJECT_CLASS_VIOLATION)
+
+#define NS_ERROR_LDAP_NOT_ALLOWED_ON_NONLEAF \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NOT_ALLOWED_ON_NONLEAF)
+
+#define NS_ERROR_LDAP_NOT_ALLOWED_ON_RDN \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NOT_ALLOWED_ON_RDN)
+
+#define NS_ERROR_LDAP_ALREADY_EXISTS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::ALREADY_EXISTS)
+
+#define NS_ERROR_LDAP_NO_OBJECT_CLASS_MODS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NO_OBJECT_CLASS_MODS)
+
+#define NS_ERROR_LDAP_RESULTS_TOO_LARGE \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::RESULTS_TOO_LARGE)
+
+#define NS_ERROR_LDAP_AFFECTS_MULTIPLE_DSAS \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::AFFECTS_MULTIPLE_DSAS)
+
+#define NS_ERROR_LDAP_OTHER \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::OTHER)
+
+#define NS_ERROR_LDAP_SERVER_DOWN \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::SERVER_DOWN)
+
+#define NS_ERROR_LDAP_LOCAL_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::LOCAL_ERROR)
+
+#define NS_ERROR_LDAP_ENCODING_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::ENCODING_ERROR)
+
+#define NS_ERROR_LDAP_DECODING_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::DECODING_ERROR)
+
+#define NS_ERROR_LDAP_TIMEOUT \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::TIMEOUT)
+
+#define NS_ERROR_LDAP_AUTH_UNKNOWN \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::AUTH_UNKNOWN)
+
+#define NS_ERROR_LDAP_FILTER_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::FILTER_ERROR)
+
+#define NS_ERROR_LDAP_USER_CANCELLED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::USER_CANCELLED)
+
+#define NS_ERROR_LDAP_PARAM_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::PARAM_ERROR)
+
+#define NS_ERROR_LDAP_NO_MEMORY \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NO_MEMORY)
+
+#define NS_ERROR_LDAP_CONNECT_ERROR \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::CONNECT_ERROR)
+
+#define NS_ERROR_LDAP_NOT_SUPPORTED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NOT_SUPPORTED)
+
+#define NS_ERROR_LDAP_CONTROL_NOT_FOUND \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::CONTROL_NOT_FOUND)
+
+#define NS_ERROR_LDAP_NO_RESULTS_RETURNED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::NO_RESULTS_RETURNED)
+
+#define NS_ERROR_LDAP_MORE_RESULTS_TO_RETURN \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::MORE_RESULTS_TO_RETURN)
+
+#define NS_ERROR_LDAP_CLIENT_LOOP \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::CLIENT_LOOP)
+
+#define NS_ERROR_LDAP_REFERRAL_LIMIT_EXCEEDED \
+ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \
+ nsILDAPErrors::REFERRAL_LIMIT_EXCEEDED)
+
+%}
diff --git a/ldap/xpcom/public/nsILDAPMessage.idl b/ldap/xpcom/public/nsILDAPMessage.idl
new file mode 100644
index 000000000..7b3298e48
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPMessage.idl
@@ -0,0 +1,170 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsILDAPBERValue;
+interface nsILDAPOperation;
+
+%{C++
+#define NS_LDAPMESSAGE_CONTRACTID "@mozilla.org/network/ldap-message;1"
+%}
+
+[scriptable, uuid(973ff50f-2002-4f0c-b57d-2242156139a2)]
+interface nsILDAPMessage : nsISupports
+{
+ /**
+ * The Distinguished Name of the entry associated with this message.
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER-decoding
+ * @exception NS_ERROR_UNEXPECTED bug or memory corruption
+ */
+ readonly attribute AUTF8String dn;
+
+ /**
+ * Get all the attributes in this message.
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ * @exception NS_ERROR_UNEXPECTED bug or memory corruption
+ * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER decoding
+ *
+ * @return array of all attributes in the current message
+ */
+ void getAttributes(out unsigned long count,
+ [retval, array, size_is(count)] out string aAttributes);
+
+ /**
+ * Get an array of all the attribute values in this message.
+ *
+ * @param attr The attribute whose values are to be returned
+ * @param count Number of values in the outbound array.
+ * @param values Array of values
+ *
+ * @exception NS_ERROR_UNEXPECTED Bug or memory corruption
+ * @exception NS_ERROR_LDAP_DECODING_ERROR Attribute not found or other
+ * decoding error.
+ * @exception NS_ERROR_OUT_OF_MEMORY
+ */
+ void getValues(in string attr, out unsigned long count,
+ [retval, array, size_is(count)] out wstring values);
+
+ /**
+ * The operation this message originated from
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to getter
+ */
+ readonly attribute nsILDAPOperation operation;
+
+ /**
+ * The result code (aka lderrno) for this message.
+ *
+ * IDL definitions for these constants live in nsILDAPErrors.idl.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ */
+ readonly attribute long errorCode;
+
+ /**
+ * The result type of this message. Possible types listed below, the
+ * values chosen are taken from the draft-ietf-ldapext-ldap-c-api-04.txt
+ * and are the same ones used in the ldap.h include file from the Mozilla
+ * LDAP C SDK.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ * @exception NS_ERROR_UNEXPECTED internal error (possible memory
+ * corruption)
+ */
+ readonly attribute long type;
+
+ /**
+ * Result of a bind operation
+ */
+ const long RES_BIND = 0x61;
+
+ /**
+ * An entry found in an search operation.
+ */
+ const long RES_SEARCH_ENTRY = 0x64;
+
+ /**
+ * An LDAPv3 search reference (a referral to another server)
+ */
+ const long RES_SEARCH_REFERENCE = 0x73;
+
+ /**
+ * The result of a search operation (i.e. the search is done; no more
+ * entries to follow).
+ */
+ const long RES_SEARCH_RESULT = 0x65;
+
+ /**
+ * The result of a modify operation.
+ */
+ const long RES_MODIFY = 0x67;
+
+ /**
+ * The result of an add operation
+ */
+ const long RES_ADD = 0x69;
+
+ /**
+ * The result of a delete operation
+ */
+ const long RES_DELETE = 0x6B;
+
+ /**
+ * The result of an modify DN operation
+ */
+ const long RES_MODDN = 0x6D;
+
+ /**
+ * The result of a compare operation
+ */
+ const long RES_COMPARE = 0x6F;
+
+ /**
+ * The result of an LDAPv3 extended operation
+ */
+ const long RES_EXTENDED = 0x78;
+
+ /**
+ * get an LDIF-like string representation of this message
+ *
+ * @return unicode encoded string representation.
+ */
+ wstring toUnicode();
+
+ /**
+ * Additional error information optionally sent by the server.
+ */
+ readonly attribute AUTF8String errorMessage;
+
+ /**
+ * In LDAPv3, when the server returns any of the following errors:
+ * NO_SUCH_OBJECT, ALIAS_PROBLEM, INVALID_DN_SYNTAX, ALIAS_DEREF_PROBLEM,
+ * it also returns the closest existing DN to the entry requested.
+ */
+ readonly attribute AUTF8String matchedDn;
+
+ /**
+ * Get an array of all the attribute values in this message (a wrapper
+ * around the LDAP C SDK's get_values_len()).
+ *
+ * @param attr The attribute whose values are to be returned
+ * @param count Number of values in the outbound array.
+ * @param values Array of nsILDAPBERValue objects
+ *
+ * @exception NS_ERROR_UNEXPECTED Bug or memory corruption
+ * @exception NS_ERROR_LDAP_DECODING_ERROR Attribute not found or other
+ * decoding error.
+ * @exception NS_ERROR_OUT_OF_MEMORY
+ */
+ void getBinaryValues(in string attr, out unsigned long count,
+ [retval, array, size_is(count)] out nsILDAPBERValue values);
+};
diff --git a/ldap/xpcom/public/nsILDAPMessageListener.idl b/ldap/xpcom/public/nsILDAPMessageListener.idl
new file mode 100644
index 000000000..8d4da5354
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPMessageListener.idl
@@ -0,0 +1,40 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsILDAPMessage;
+
+interface nsILDAPConnection;
+
+/**
+ * A callback interface to be implemented by any objects that want to
+ * receive results from an nsILDAPOperation (ie nsILDAPMessages) as they
+ * come in.
+ */
+[scriptable, uuid(dc721d4b-3ff2-4387-a80c-5e29545f774a)]
+interface nsILDAPMessageListener : nsISupports
+{
+ /**
+ * Messages received are passed back via this function.
+ *
+ * @arg aMessage The message that was returned, NULL if none was.
+ *
+ * XXX semantics of NULL?
+ */
+ void onLDAPMessage(in nsILDAPMessage aMessage);
+
+ /**
+ * Notify the listener that the Init has completed, passing
+ * in the results from the connection initialization. The
+ * Reason for this is to allow us to do asynchronous DNS
+ * lookups, preresolving hostnames.
+ *
+ * @arg aConn The LDAP connection in question
+ * @arg aStatus The result from the LDAP connection init
+ */
+ void onLDAPInit(in nsILDAPConnection aConn, in nsresult aStatus);
+};
diff --git a/ldap/xpcom/public/nsILDAPModification.idl b/ldap/xpcom/public/nsILDAPModification.idl
new file mode 100644
index 000000000..6c93316e9
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPModification.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsILDAPBERValue;
+interface nsIArray;
+
+[scriptable, uuid(f64ef501-0623-11d6-a7f2-b65476fc49dc)]
+interface nsILDAPModification : nsISupports
+{
+ /**
+ * The operation to perform.
+ */
+ attribute long operation;
+
+ /**
+ * Add operation
+ */
+ const long MOD_ADD = 0x00;
+
+ /**
+ * Delete operation
+ */
+ const long MOD_DELETE = 0x01;
+
+ /**
+ * Replace operation
+ */
+ const long MOD_REPLACE = 0x02;
+
+ /**
+ * Values are BER encoded
+ */
+ const long MOD_BVALUES = 0x80;
+
+ /**
+ * The attribute to modify.
+ */
+ attribute ACString type;
+
+ /**
+ * The array of values this modification sets for the attribute
+ */
+ attribute nsIArray values;
+
+ /**
+ * Function that allows all the attributes to be set at the same
+ * time to avoid multiple function calls.
+ */
+ void setUpModification(in long aOperation, in ACString aType,
+ in nsIArray aValues);
+
+ void setUpModificationOneValue(in long aOperation, in ACString aType,
+ in nsILDAPBERValue aValue);
+};
diff --git a/ldap/xpcom/public/nsILDAPOperation.idl b/ldap/xpcom/public/nsILDAPOperation.idl
new file mode 100644
index 000000000..f3b9e6e7a
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPOperation.idl
@@ -0,0 +1,275 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsILDAPConnection.idl"
+#include "nsIAuthModule.idl"
+
+interface nsILDAPMessage;
+interface nsILDAPMessageListener;
+interface nsILDAPModification;
+interface nsIMutableArray;
+interface nsIArray;
+
+%{C++
+#define NS_LDAPOPERATION_CONTRACTID "@mozilla.org/network/ldap-operation;1"
+%}
+
+// XXXdmose check to make sure ctl-related err codes documented
+
+typedef uint32_t PRIntervalTime;
+
+[scriptable, uuid(4dfb1b19-fc8f-4525-92e7-f97b78a9747a)]
+interface nsILDAPOperation : nsISupports
+{
+ /**
+ * The connection this operation is on.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in
+ */
+ readonly attribute nsILDAPConnection connection;
+
+ /**
+ * Callback for individual result messages related to this operation (set
+ * by the init() method). This is actually an nsISupports proxy object,
+ * as the callback will happen from another thread.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in
+ */
+ readonly attribute nsILDAPMessageListener messageListener;
+
+ /**
+ * The message-id associated with this operation.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in
+ */
+ readonly attribute long messageID;
+
+ /**
+ * private parameter (anything caller desires)
+ */
+ attribute nsISupports closure;
+
+ /**
+ * No time and/or size limit specified
+ */
+ const long NO_LIMIT = 0;
+
+ /**
+ * If specified, these arrays of nsILDAPControls are passed into the LDAP
+ * C SDK for any extended operations (ie method calls on this interface
+ * ending in "Ext").
+ */
+ attribute nsIMutableArray serverControls;
+ attribute nsIMutableArray clientControls;
+
+ /**
+ * Initializes this operation. Must be called prior to initiating
+ * any actual operations. Note that by default, the aMessageListener
+ * callbacks happen on the LDAP connection thread. If you need them
+ * to happen on the main thread (or any other thread), then you should
+ * created an nsISupports proxy object and pass that in.
+ *
+ * @param aConnection connection this operation should use
+ * @param aMessageListener interface used to call back the results.
+ * @param aClosure private parameter (anything caller desires)
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in
+ * @exception NS_ERROR_UNEXPECTED failed to get connection handle
+ */
+ void init(in nsILDAPConnection aConnection,
+ in nsILDAPMessageListener aMessageListener,
+ in nsISupports aClosure);
+
+ /**
+ * Asynchronously authenticate to the LDAP server.
+ *
+ * @param passwd the password used for binding; NULL for anon-binds
+ *
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR problem encoding bind request
+ * @exception NS_ERROR_LDAP_SERVER_DOWN server down (XXX rebinds?)
+ * @exception NS_ERROR_LDAP_CONNECT_ERROR connection failed or lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_UNEXPECTED internal error
+ */
+ void simpleBind(in AUTF8String passwd);
+
+ /**
+ * Asynchronously perform a SASL bind against the LDAP server
+ *
+ * @param service the host name of the service being connected to
+ * @param mechanism the name of the SASL mechanism in use
+ * @param authModule the nsIAuthModule to be used to perform the operation
+ *
+ */
+ void saslBind(in ACString service, in ACString mechanism,
+ in nsIAuthModule authModule);
+
+ /**
+ * Continue a SASL bind operation
+ *
+ * @param token the next SASL token to send to the server
+ * @param tokenLen the length of the token to send
+ *
+ */
+ void saslStep(in string token, in unsigned long tokenLen);
+
+ /**
+ * Kicks off an asynchronous add request. The "ext" stands for
+ * "extensions", and is intended to convey that this method will
+ * eventually support the extensions described in the
+ * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft.
+ *
+ * @param aBaseDn Base DN to add
+ * @param aModCount Number of modifications
+ * @param aMods Array of modifications
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version
+ * of the LDAP protocol that the
+ * client is using
+ * @exception NS_ERROR_UNEXPECTED an unexpected error has
+ * occurred
+ *
+ * XXX doesn't currently handle LDAPControl params
+ */
+ void addExt(in AUTF8String aBaseDn, in nsIArray aMods);
+
+ /**
+ * Kicks off an asynchronous delete request. The "ext" stands for
+ * "extensions", and is intended to convey that this method will
+ * eventually support the extensions described in the
+ * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft.
+ *
+ * @param aBaseDn Base DN to delete
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version
+ * of the LDAP protocol that the
+ * client is using
+ * @exception NS_ERROR_UNEXPECTED an unexpected error has
+ * occurred
+ *
+ * XXX doesn't currently handle LDAPControl params
+ */
+ void deleteExt(in AUTF8String aBaseDn);
+
+ /**
+ * Kicks off an asynchronous modify request. The "ext" stands for
+ * "extensions", and is intended to convey that this method will
+ * eventually support the extensions described in the
+ * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft.
+ *
+ * @param aBaseDn Base DN to modify
+ * @param aModCount Number of modifications
+ * @param aMods Array of modifications
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version
+ * of the LDAP protocol that the
+ * client is using
+ * @exception NS_ERROR_UNEXPECTED an unexpected error has
+ * occurred
+ *
+ * XXX doesn't currently handle LDAPControl params
+ */
+ void modifyExt(in AUTF8String aBaseDn, in nsIArray aMods);
+
+ /**
+ * Kicks off an asynchronous rename request.
+ *
+ * @param aBaseDn Base DN to rename
+ * @param aNewRDn New relative DN
+ * @param aNewParent DN of the new parent under which to move the
+ * entry
+ * @param aDeleteOldRDn Indicates whether to remove the old relative
+ * DN as a value in the entry or not
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version
+ * of the LDAP protocol that the
+ * client is using
+ * @exception NS_ERROR_UNEXPECTED an unexpected error has
+ * occurred
+ *
+ * XXX doesn't currently handle LDAPControl params
+ */
+ void rename(in AUTF8String aBaseDn, in AUTF8String aNewRDn,
+ in AUTF8String aNewParent, in boolean aDeleteOldRDn);
+
+ /**
+ * Kicks off an asynchronous search request. The "ext" stands for
+ * "extensions", and is intended to convey that this method will
+ * eventually support the extensions described in the
+ * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft.
+ *
+ * @param aBaseDn Base DN to search
+ * @param aScope One of SCOPE_{BASE,ONELEVEL,SUBTREE}
+ * @param aFilter Search filter
+ * @param aAttributes Comma separated list of values, holding the
+ * attributes we need
+ * @param aTimeOut How long to wait
+ * @param aSizeLimit Maximum number of entries to return.
+ *
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version
+ * of the LDAP protocol that the
+ * client is using
+ * @exception NS_ERROR_LDAP_FILTER_ERROR
+ * @exception NS_ERROR_UNEXPECTED
+ */
+ void searchExt(in AUTF8String aBaseDn, in int32_t aScope,
+ in AUTF8String aFilter, in ACString aAttributes,
+ in PRIntervalTime aTimeOut, in int32_t aSizeLimit);
+
+ /**
+ * Cancels an async operation that is in progress.
+ *
+ * XXX controls not supported yet
+ *
+ * @exception NS_ERROR_NOT_IMPLEMENTED server or client controls
+ * were set on this object
+ * @exception NS_ERROR_NOT_INITIALIZED operation not initialized
+ * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding
+ * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not
+ * receive the request or the
+ * connection was lost
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ * @exception NS_ERROR_INVALID_ARG invalid argument
+ * @exception NS_ERROR_UNEXPECTED internal error
+ */
+ void abandonExt();
+};
diff --git a/ldap/xpcom/public/nsILDAPServer.idl b/ldap/xpcom/public/nsILDAPServer.idl
new file mode 100644
index 000000000..5fc9522e4
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPServer.idl
@@ -0,0 +1,86 @@
+/* -*- 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 "nsISupports.idl"
+#include "nsILDAPConnection.idl"
+
+interface nsILDAPURL;
+
+/**
+ * this interface provides a way to store, retrieve and manipulate
+ * information related to a specific LDAP server. This includes the
+ * LDAP URL, as well as certain user specific data (e.g. credentials).
+ *
+ * The implementation of nsILDAPService relies heavily on this
+ * interface, managing all LDAP connections (nsILDAPConnection).
+ * The Service manages LDAP connections (connect and disconnect etc.),
+ * using the information available from these LDAP Server objects.
+ */
+
+
+[scriptable, uuid(8aa717a4-1dd2-11b2-99c7-f01e2d449ded)]
+interface nsILDAPServer : nsISupports {
+
+ /**
+ * unique identifier for this server, used (typically) to identify a
+ * particular server object in a list of servers. This key can be
+ * any "string", but in our case it will most likely be the same
+ * identifier as used in a Mozilla preferences files.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ attribute wstring key;
+
+ /**
+ * the password string used to bind to this server. An empty
+ * string here implies binding as anonymous.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ attribute AUTF8String password;
+
+ /**
+ * the user name to authenticate as. An empty string here would
+ * imply binding as anonymous.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ attribute AUTF8String username;
+
+ /**
+ * the bind DN (Distinguished Name).
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ attribute AUTF8String binddn;
+
+ /** maximum number of hits we want to accept from an LDAP search
+ * operation.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ */
+ attribute unsigned long sizelimit;
+
+ /**
+ * the URL for this server.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ */
+ attribute nsILDAPURL url;
+
+ /**
+ * protocol version to be used (see nsILDAPConnection.idl for constants)
+ * Defaults to 3.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer passed to getter
+ * @exception NS_ERROR_INVALID_ARG Invalid version passed to setter
+ */
+ attribute unsigned long protocolVersion;
+};
diff --git a/ldap/xpcom/public/nsILDAPService.idl b/ldap/xpcom/public/nsILDAPService.idl
new file mode 100644
index 000000000..e4f8e75e6
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPService.idl
@@ -0,0 +1,197 @@
+/* -*- 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 "nsISupports.idl"
+interface nsILDAPServer;
+interface nsILDAPConnection;
+interface nsILDAPMessageListener;
+
+/**
+ * This interface provides an LDAP connection management service.
+ * It's used to cache already established LDAP connections, as well
+ * as reaping unused connections after a certain time period. This
+ * is done completely asynchronously, using callback functions.
+ */
+
+
+[scriptable, uuid(69de6fbc-2e8c-4482-bf14-358d68b785d1)]
+interface nsILDAPService : nsISupports {
+
+ /**
+ * Add a (possibly) new LDAP server entry to the service. A
+ * server entry holds information about the host, port and
+ * other components of the LDAP URL, as well as information
+ * used for binding a connection to the LDAP server.
+ *
+ * An LDAP Server entry (nsILDAPServer) contains the URL,
+ * user credentials, and other information related to the actual
+ * server itself. It is used for connecting, binding, rebinding,
+ * setting timeouts and so forth.
+ *
+ * @param aServer an nsILDAPServer object
+ *
+ * @exception NS_ERROR_FAILURE the server has already been
+ * added to the service
+ * @exception NS_ERROR_NULL_POINTER NULL pointer
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ void addServer(in nsILDAPServer aServer);
+
+ /**
+ * Mark an LDAP server, in the Service, as a candidate for
+ * deletion. If there are still leases ("users") of this server,
+ * the operation fails.
+ *
+ * @param aKey unique key identifying the server entry
+ *
+ * @exception NS_ERROR_FAILURE either the server doesn't
+ * exist, or there are still
+ * leases oustanding
+ */
+ void deleteServer(in wstring aKey);
+
+ /**
+ * Get the nsILDAPServer object for the specified server entry
+ * in the service.
+ *
+ * @param aKey unique key identifying the server entry
+ *
+ * @exception NS_ERROR_FAILURE there is no server registered
+ * in the service with this key
+ * @exception NS_ERROR_NULL_POINTER NULL pointer
+ */
+ nsILDAPServer getServer(in wstring aKey);
+
+ /**
+ * Request a connection from the service, asynchronously. If there is
+ * one "cached" already, we will actually call the callback function
+ * before returning from this function. This might be considered a bug,
+ * but for now be aware of this (see Bugzilla bug #75989).
+ *
+ * Calling this method does not increment the leases on this connection,
+ * you'll have to use the getConnection() method to actually get the
+ * connection itself (presumably from the callback/listener object).
+ * The listener needs to implement nsILDAPMessageListener, providing
+ * the OnLDAPMessage() method.
+ *
+ * @param aKey unique key identifying the server entry
+ * @param aMessageListener the listener object, which we will call
+ * when the LDAP bind message is available
+ *
+ * @exception NS_ERROR_FAILURE there is no server registered
+ * in the service with this key,
+ * or we were unable to get a
+ * connection properly to the server
+ * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_UNEXPECTED unknown or unexpected error...
+ */
+ void requestConnection(in wstring aKey,
+ in nsILDAPMessageListener aListener);
+
+ /**
+ * This is the nsLDAPConnection object related to this server.
+ * This does increase the lease counter on the object, so you have
+ * to call the releaseConnection() method to return it. It is
+ * important that you do this in matching pairs, and that you do
+ * not keep any dangling references to an object around after you
+ * have called the releaseConnection() method.
+ *
+ * @param aKey unique key identifying the server entry
+ *
+ * @exception NS_ERROR_FAILURE there is no server registered
+ * in the service with this key
+ * @exception NS_ERROR_NULL_POINTER NULL pointer
+ */
+ nsILDAPConnection getConnection(in wstring aKey);
+
+ /**
+ * Release the lease on a (cached) LDAP connection, making it a
+ * potential candidate for disconnection. Note that this will not
+ * delete the actual LDAP server entry in the service, it's still
+ * registered and can be used in future calls to requestConnection().
+ *
+ * This API might be deprecated in the future, once we figure out how
+ * to use weak references to support our special needs for reference
+ * counting. For the time being, it is vital that you call this function
+ * when you're done with a Connection, and that you do not keep any
+ * copies of the Connection object lingering around.
+ *
+ * @param aKey unique key identifying the server entry
+ *
+ * @exception NS_ERROR_FAILURE there is no server registered
+ * in the service with this key
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+ void releaseConnection(in wstring aKey);
+
+ /**
+ * If we detect that a connection is broken (server disconnected us,
+ * or any other problem with the link), we need to try to reestablish
+ * the connection. This is very similar to requestConnection(),
+ * except you use this when detecting an error with a connection
+ * that is being cached.
+ *
+ * @param aKey unique key identifying the server entry
+ * @param aMessageListener the listener object, which we will call
+ * when the LDAP bind message is available
+ *
+ * @exception NS_ERROR_FAILURE there is no server registered
+ * in the service with this key,
+ * or we were unable to get a
+ * connection properly to the server
+ * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ * @exception NS_ERROR_UNEXPECTED unknown or unexpected error...
+ */
+ void reconnectConnection(in wstring aKey,
+ in nsILDAPMessageListener aListener);
+
+ /**
+ * Generates and returns an LDAP search filter by substituting
+ * aValue, aAttr, aPrefix, and aSuffix into aPattern.
+ *
+ * The only good documentation I'm aware of for this function is
+ * at <http://docs.iplanet.com/docs/manuals/dirsdk/csdk41/html/filter.htm>
+ * and
+ * <http://docs.iplanet.com/docs/manuals/dirsdk/csdk41/html/function.htm#17835>
+ * Unfortunately, this does not currently seem to be available
+ * under any open source license, so I can't include that
+ * documentation here in the doxygen comments.
+ *
+ * @param aMaxSize maximum size (in char) of string to be
+ * created and returned (including final \0)
+ * @param aPattern pattern to be used for the filter
+ * @param aPrefix prefix to prepend to the filter
+ * @param aSuffix suffix to be appended to the filer
+ * @param aAttr replacement for %a in the pattern
+ * @param aValue replacement for %v in the pattern
+ *
+ * @exception NS_ERROR_INVALID_ARG invalid parameter passed in
+ * @exception NS_ERROR_OUT_OF_MEMORY allocation failed
+ * @exception NS_ERROR_NOT_AVAILABLE filter longer than maxsiz chars
+ * @exception NS_ERROR_UNEXPECTED ldap_create_filter returned
+ * unexpected error code
+ */
+ AUTF8String createFilter(in unsigned long aMaxSize, in AUTF8String aPattern,
+ in AUTF8String aPrefix, in AUTF8String aSuffix,
+ in AUTF8String aAttr, in AUTF8String aValue);
+
+ /**
+ * Parses a distinguished name (DN) and returns the relative DN,
+ * base DN and the list of attributes that make up the relative DN.
+ *
+ * @param dn DN to parse
+ * @param rdn The relative DN for the given DN
+ * @param baseDn The base DN for the given DN
+ * @param rdnCount Number of values in the outbound attributes array.
+ * @param rdnAttrs Array of attribute names
+ *
+ */
+ void parseDn(in string dn, out AUTF8String rdn, out AUTF8String baseDn,
+ out unsigned long rdnCount,
+ [retval, array, size_is(rdnCount)] out string rdnAttrs);
+};
diff --git a/ldap/xpcom/public/nsILDAPSyncQuery.idl b/ldap/xpcom/public/nsILDAPSyncQuery.idl
new file mode 100644
index 000000000..2ae3307f0
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPSyncQuery.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* 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 "nsISupports.idl"
+interface nsILDAPURL;
+
+
+[scriptable, uuid (0308fb36-1dd2-11b2-b16f-8510e8c5311a)]
+interface nsILDAPSyncQuery : nsISupports {
+
+ /**
+ * getQueryResults
+ *
+ * Create a new LDAP connection do a synchronous LDAP search and return
+ * the results.
+ * @param aServerURL - LDAP URL with parameters to a LDAP search
+ * ("ldap://host/base?attributes?one/sub?filter")
+ * @param aProtocolVersion - LDAP protocol version to use for connection
+ * (nsILDAPConnection.idl has symbolic constants)
+ * @return results
+ */
+ wstring getQueryResults (in nsILDAPURL aServerURL,
+ in unsigned long aProtocolVersion);
+
+};
diff --git a/ldap/xpcom/public/nsILDAPURL.idl b/ldap/xpcom/public/nsILDAPURL.idl
new file mode 100644
index 000000000..dae76496d
--- /dev/null
+++ b/ldap/xpcom/public/nsILDAPURL.idl
@@ -0,0 +1,132 @@
+/* -*- 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 "nsIURI.idl"
+
+%{C++
+#define NS_LDAPURL_CONTRACTID "@mozilla.org/network/ldap-url;1"
+%}
+
+/**
+ * Strings in methods inherited from nsIURI, which are using XPIDL
+ * |string| types, are expected to be UTF8 encoded. All such strings
+ * in this interface, except attribute types (e.g. "cn"), should in fact
+ * be UTF8. It's important to remember that attributes can not be UTF8,
+ * they can only be of a limited subset of ASCII (see RFC 2251).
+ */
+
+[scriptable, uuid(8e3a6d33-2e68-40ba-8f94-6ac03f69066e)]
+interface nsILDAPURL : nsIURI {
+ /**
+ * Initialize an LDAP URL
+ *
+ * @param aUrlType - one of the URLTYPE_ flags @seealso nsIStandardURL
+ * @param aDefaultPort - if the port parsed from the URL string matches
+ * this port, then the port will be removed from the
+ * canonical form of the URL.
+ * @param aSpec - URL string.
+ * @param aOriginCharset - the charset from which this URI string
+ * originated. this corresponds to the charset
+ * that should be used when communicating this
+ * URI to an origin server, for example. if
+ * null, then provide aBaseURI implements this
+ * interface, the origin charset of aBaseURI will
+ * be assumed, otherwise defaulting to UTF-8 (i.e.,
+ * no charset transformation from aSpec).
+ * @param aBaseURI - if null, aSpec must specify an absolute URI.
+ * otherwise, aSpec will be resolved relative
+ * to aBaseURI.
+ */
+ void init(in unsigned long aUrlType,
+ in long aDefaultPort,
+ in AUTF8String aSpec,
+ in string aOriginCharset,
+ in nsIURI aBaseURI);
+
+ /**
+ * The distinguished name of the URL (ie the base DN for the search).
+ * This string is expected to be a valid UTF8 string.
+ *
+ * for the getter:
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory
+ */
+ attribute AUTF8String dn;
+
+ /**
+ * The attributes to get for this URL, in comma-separated format. If the
+ * list is empty, all attributes are requested.
+ */
+ attribute ACString attributes;
+
+ /**
+ * Add one attribute to the array of attributes to request. If the
+ * attribute is already in our array, this becomes a noop.
+ *
+ * @param aAttribute An LDAP attribute (e.g. "cn")
+ */
+ void addAttribute(in ACString aAttribute);
+
+ /**
+ * Remove one attribute from the array of attributes to request. If
+ * the attribute didn't exist in the array, this becomes a noop.
+ *
+ * @param aAttribute An LDAP attribute (e.g. "cn")
+ * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory
+ */
+ void removeAttribute(in ACString aAttribute);
+
+ /**
+ * Test if an attribute is in our list of attributes already
+ *
+ * @param aAttribute An LDAP attribute (e.g. "cn")
+ * @return boolean Truth value
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ */
+ boolean hasAttribute(in ACString aAttribute);
+
+ /**
+ * The scope of the search. defaults to SCOPE_BASE.
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_MALFORMED_URI Illegal base to SET method
+ */
+ attribute long scope;
+
+ /**
+ * Search just the base object
+ */
+ const long SCOPE_BASE = 0;
+
+ /**
+ * Search only the children of the base object
+ */
+ const long SCOPE_ONELEVEL = 1;
+
+ /**
+ * Search the entire subtree under and including the base object
+ */
+ const long SCOPE_SUBTREE = 2;
+
+ /**
+ * The search filter. "(objectClass=*)" is the default.
+ */
+ attribute AUTF8String filter;
+
+ /**
+ * Any options defined for this URL (check options using a bitwise and)
+ *
+ * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method
+ * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory
+ */
+ attribute unsigned long options;
+
+ /**
+ * If this is set/true, this is an ldaps: URL, not an ldap: URL
+ */
+ const unsigned long OPT_SECURE = 0x01;
+};
diff --git a/ldap/xpcom/src/ldapComponents.manifest b/ldap/xpcom/src/ldapComponents.manifest
new file mode 100644
index 000000000..482bbd55e
--- /dev/null
+++ b/ldap/xpcom/src/ldapComponents.manifest
@@ -0,0 +1,5 @@
+component {b3de9249-b0e5-4c12-8d91-c9a434fd80f5} nsLDAPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=ldap {b3de9249-b0e5-4c12-8d91-c9a434fd80f5}
+
+component {c85a5ef2-9c56-445f-b029-76889f2dd29b} nsLDAPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=ldaps {c85a5ef2-9c56-445f-b029-76889f2dd29b}
diff --git a/ldap/xpcom/src/moz.build b/ldap/xpcom/src/moz.build
new file mode 100644
index 000000000..dc63d6118
--- /dev/null
+++ b/ldap/xpcom/src/moz.build
@@ -0,0 +1,53 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsLDAPBERElement.cpp',
+ 'nsLDAPBERValue.cpp',
+ 'nsLDAPConnection.cpp',
+ 'nsLDAPControl.cpp',
+ 'nsLDAPMessage.cpp',
+ 'nsLDAPModification.cpp',
+ 'nsLDAPOperation.cpp',
+ 'nsLDAPProtocolModule.cpp',
+ 'nsLDAPSecurityGlue.cpp',
+ 'nsLDAPServer.cpp',
+ 'nsLDAPService.cpp',
+ 'nsLDAPURL.cpp',
+]
+
+if CONFIG['MOZ_PREF_EXTENSIONS']:
+ SOURCES += ['nsLDAPSyncQuery.cpp']
+ DEFINES['MOZ_PREF_EXTENSIONS'] = True
+
+EXTRA_COMPONENTS += [
+ 'ldapComponents.manifest',
+ 'nsLDAPProtocolHandler.js',
+]
+
+USE_LIBS += [
+ 'ldapsdks',
+]
+
+if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']:
+ XPCOMBinaryComponent('mozldap')
+ USE_LIBS += [
+ 'nspr',
+ 'xpcomglue_s',
+ 'xul',
+ ]
+# js needs to come after xul for now, because it is an archive and its content
+# is discarded when it comes first.
+ USE_LIBS += [
+ 'js',
+ ]
+else:
+ Library('mozldap')
+ FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../../c-sdk/include',
+]
+
diff --git a/ldap/xpcom/src/nsLDAPBERElement.cpp b/ldap/xpcom/src/nsLDAPBERElement.cpp
new file mode 100644
index 000000000..1ea96793e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERElement.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsLDAPBERElement.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsLDAPBERValue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPBERElement, nsILDAPBERElement)
+
+nsLDAPBERElement::nsLDAPBERElement()
+ : mElement(0)
+{
+}
+
+nsLDAPBERElement::~nsLDAPBERElement()
+{
+ if (mElement) {
+ // anything inside here is not something that we own separately from
+ // this object, so free it
+ ber_free(mElement, 1);
+ }
+
+ return;
+}
+
+NS_IMETHODIMP
+nsLDAPBERElement::Init(nsILDAPBERValue *aValue)
+{
+ if (aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ mElement = ber_alloc_t(LBER_USE_DER);
+ return mElement ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+/* void putString (in AUTF8String aString, in unsigned long aTag); */
+NS_IMETHODIMP
+nsLDAPBERElement::PutString(const nsACString & aString, uint32_t aTag,
+ uint32_t *aBytesWritten)
+{
+ // XXX if the string translation feature of the C SDK is ever used,
+ // this const_cast will break
+ int i = ber_put_ostring(mElement,
+ const_cast<char *>(PromiseFlatCString(aString).get()),
+ aString.Length(), aTag);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aBytesWritten = i;
+ return NS_OK;
+}
+
+/* void startSet (); */
+NS_IMETHODIMP nsLDAPBERElement::StartSet(uint32_t aTag)
+{
+ int i = ber_start_set(mElement, aTag);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+/* void putSet (); */
+NS_IMETHODIMP nsLDAPBERElement::PutSet(uint32_t *aBytesWritten)
+{
+ int i = ber_put_set(mElement);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aBytesWritten = i;
+ return NS_OK;
+}
+
+/* nsILDAPBERValue flatten (); */
+NS_IMETHODIMP nsLDAPBERElement::GetAsValue(nsILDAPBERValue **_retval)
+{
+ // create the value object
+ nsCOMPtr<nsILDAPBERValue> berValue = new nsLDAPBERValue();
+
+ if (!berValue) {
+ NS_ERROR("nsLDAPBERElement::GetAsValue(): out of memory"
+ " creating nsLDAPBERValue object");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ struct berval *bv;
+ if ( ber_flatten(mElement, &bv) < 0 ) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = berValue->Set(bv->bv_len,
+ reinterpret_cast<uint8_t *>(bv->bv_val));
+
+ // whether or not we've succeeded, we're done with the ldap c sdk struct
+ ber_bvfree(bv);
+
+ // as of this writing, this error can only be NS_ERROR_OUT_OF_MEMORY
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // return the raw interface pointer
+ NS_ADDREF(*_retval = berValue.get());
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPBERElement.h b/ldap/xpcom/src/nsLDAPBERElement.h
new file mode 100644
index 000000000..bdeb9c439
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERElement.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "lber.h"
+#include "nsILDAPBERElement.h"
+
+// 070af769-b7f5-40e7-81be-196155ead84c
+#define NS_LDAPBERELEMENT_CID \
+ { 0x070af769, 0xb7f5, 0x40e7, \
+ { 0x81, 0xbe, 0x19, 0x61, 0x55, 0xea, 0xd8, 0x4c }}
+
+class nsLDAPBERElement final : public nsILDAPBERElement
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILDAPBERELEMENT
+
+ nsLDAPBERElement();
+
+private:
+ ~nsLDAPBERElement();
+
+ BerElement *mElement;
+
+protected:
+};
+
diff --git a/ldap/xpcom/src/nsLDAPBERValue.cpp b/ldap/xpcom/src/nsLDAPBERValue.cpp
new file mode 100644
index 000000000..d71073b7a
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERValue.cpp
@@ -0,0 +1,106 @@
+/* -*- 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 "nsLDAPBERValue.h"
+#include "nsMemory.h"
+#include "nsStringGlue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPBERValue, nsILDAPBERValue)
+
+nsLDAPBERValue::nsLDAPBERValue() : mValue(0), mSize(0)
+{
+}
+
+nsLDAPBERValue::~nsLDAPBERValue()
+{
+ if (mValue) {
+ free(mValue);
+ }
+}
+
+// void get (out unsigned long aCount,
+// [array, size_is (aCount), retval] out octet aRetVal); */
+NS_IMETHODIMP
+nsLDAPBERValue::Get(uint32_t *aCount, uint8_t **aRetVal)
+{
+ // if mSize = 0, return a count of a 0 and a null pointer
+
+ if (mSize) {
+ // get a buffer to hold a copy of the data
+ //
+ uint8_t *array = static_cast<uint8_t *>(moz_xmalloc(mSize));
+
+ if (!array) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // copy and return
+ //
+ memcpy(array, mValue, mSize);
+ *aRetVal = array;
+ } else {
+ *aRetVal = 0;
+ }
+
+ *aCount = mSize;
+ return NS_OK;
+}
+
+// void set(in unsigned long aCount,
+// [array, size_is(aCount)] in octet aValue);
+NS_IMETHODIMP
+nsLDAPBERValue::Set(uint32_t aCount, uint8_t *aValue)
+{
+ // get rid of any old value being held here
+ //
+ if (mValue) {
+ free(mValue);
+ }
+
+ // if this is a non-zero value, allocate a buffer and copy
+ //
+ if (aCount) {
+ // get a buffer to hold a copy of this data
+ //
+ mValue = static_cast<uint8_t *>(moz_xmalloc(aCount));
+ if (!mValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // copy the data and return
+ //
+ memcpy(mValue, aValue, aCount);
+ } else {
+ // otherwise just set it to null
+ //
+ mValue = 0;
+ }
+
+ mSize = aCount;
+ return NS_OK;
+}
+
+// void setFromUTF8(in AUTF8String aValue);
+//
+NS_IMETHODIMP
+nsLDAPBERValue::SetFromUTF8(const nsACString & aValue)
+{
+ // get rid of any old value being held here
+ //
+ if (mValue) {
+ free(mValue);
+ }
+
+ // copy the data and return
+ //
+ mSize = aValue.Length();
+ if (mSize) {
+ mValue = reinterpret_cast<uint8_t *>(ToNewCString(aValue));
+ } else {
+ mValue = 0;
+ }
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPBERValue.h b/ldap/xpcom/src/nsLDAPBERValue.h
new file mode 100644
index 000000000..d7f563f03
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERValue.h
@@ -0,0 +1,40 @@
+/* -*- 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/. */
+
+#ifndef _nsLDAPBERValue_h_
+#define _nsLDAPBERValue_h_
+
+#include "ldap.h"
+#include "nsILDAPBERValue.h"
+
+// 7c9fa10e-1dd2-11b2-a097-ac379e6803b2
+//
+#define NS_LDAPBERVALUE_CID \
+{ 0x7c9fa10e, 0x1dd2, 0x11b2, \
+ {0xa0, 0x97, 0xac, 0x37, 0x9e, 0x68, 0x03, 0xb2 }}
+
+class nsLDAPBERValue : public nsILDAPBERValue
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPBERVALUE
+
+ nsLDAPBERValue();
+
+protected:
+ virtual ~nsLDAPBERValue();
+
+ /**
+ * nsLDAPControl needs to be able to grovel through this without an
+ * an extra copy
+ */
+ friend class nsLDAPControl;
+
+ uint8_t *mValue; // pointer to an array
+ uint32_t mSize; // size of the value, in bytes
+};
+
+#endif // _nsLDAPBERValue_h_
diff --git a/ldap/xpcom/src/nsLDAPConnection.cpp b/ldap/xpcom/src/nsLDAPConnection.cpp
new file mode 100644
index 000000000..834e803b4
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPConnection.cpp
@@ -0,0 +1,737 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsIServiceManager.h"
+#include "nsStringGlue.h"
+#include "nsIComponentManager.h"
+#include "nsIDNSRecord.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPMessage.h"
+#include "nsThreadUtils.h"
+#include "nsIConsoleService.h"
+#include "nsIDNSService.h"
+#include "nsINetAddr.h"
+#include "nsIRequestObserver.h"
+#include "nsError.h"
+#include "nsLDAPOperation.h"
+#include "nsILDAPErrors.h"
+#include "nsIClassInfoImpl.h"
+#include "nsILDAPURL.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsMemory.h"
+#include "nsLDAPUtils.h"
+#include "nsProxyRelease.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+
+using namespace mozilla;
+
+const char kDNSServiceContractId[] = "@mozilla.org/network/dns-service;1";
+
+// constructor
+//
+nsLDAPConnection::nsLDAPConnection()
+ : mConnectionHandle(nullptr),
+ mPendingOperationsMutex("nsLDAPConnection.mPendingOperationsMutex"),
+ mPendingOperations(10),
+ mSSL(false),
+ mVersion(nsILDAPConnection::VERSION3),
+ mDNSRequest(nullptr)
+{
+}
+
+// destructor
+//
+nsLDAPConnection::~nsLDAPConnection()
+{
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+ if (obsServ)
+ obsServ->RemoveObserver(this, "profile-change-net-teardown");
+ Close();
+}
+
+NS_IMPL_ADDREF(nsLDAPConnection)
+NS_IMPL_RELEASE(nsLDAPConnection)
+NS_IMPL_CLASSINFO(nsLDAPConnection, NULL, nsIClassInfo::THREADSAFE,
+ NS_LDAPCONNECTION_CID)
+
+NS_INTERFACE_MAP_BEGIN(nsLDAPConnection)
+ NS_INTERFACE_MAP_ENTRY(nsILDAPConnection)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPConnection)
+ NS_IMPL_QUERY_CLASSINFO(nsLDAPConnection)
+NS_INTERFACE_MAP_END_THREADSAFE
+NS_IMPL_CI_INTERFACE_GETTER(nsLDAPConnection, nsILDAPConnection,
+ nsISupportsWeakReference, nsIDNSListener,
+ nsIObserver)
+
+NS_IMETHODIMP
+nsLDAPConnection::Init(nsILDAPURL *aUrl, const nsACString &aBindName,
+ nsILDAPMessageListener *aMessageListener,
+ nsISupports *aClosure, uint32_t aVersion)
+{
+ NS_ENSURE_ARG_POINTER(aUrl);
+ NS_ENSURE_ARG_POINTER(aMessageListener);
+
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // We have to abort all LDAP pending operation before shutdown.
+ obsServ->AddObserver(this, "profile-change-net-teardown", true);
+
+ // Save various items that we'll use later
+ mBindName.Assign(aBindName);
+ mClosure = aClosure;
+ mInitListener = aMessageListener;
+
+ // Make sure we haven't called Init earlier, i.e. there's a DNS
+ // request pending.
+ NS_ASSERTION(!mDNSRequest, "nsLDAPConnection::Init() "
+ "Connection was already initialized\n");
+
+ // Check and save the version number
+ if (aVersion != nsILDAPConnection::VERSION2 &&
+ aVersion != nsILDAPConnection::VERSION3) {
+ NS_ERROR("nsLDAPConnection::Init(): illegal version");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mVersion = aVersion;
+
+ // Get the port number, SSL flag for use later, once the DNS server(s)
+ // has resolved the host part.
+ rv = aUrl->GetPort(&mPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t options;
+ rv = aUrl->GetOptions(&options);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSSL = options & nsILDAPURL::OPT_SECURE;
+
+ nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
+ if (!curThread) {
+ NS_ERROR("nsLDAPConnection::Init(): couldn't get current thread");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Do the pre-resolve of the hostname, using the DNS service. This
+ // will also initialize the LDAP connection properly, once we have
+ // the IPs resolved for the hostname. This includes creating the
+ // new thread for this connection.
+ //
+ // XXX - What return codes can we expect from the DNS service?
+ //
+ nsCOMPtr<nsIDNSService>
+ pDNSService(do_GetService(kDNSServiceContractId, &rv));
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPConnection::Init(): couldn't create the DNS Service object");
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = aUrl->GetAsciiHost(mDNSHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if the caller has passed in a space-delimited set of hosts, as the
+ // ldap c-sdk allows, strip off the trailing hosts for now.
+ // Soon, we'd like to make multiple hosts work, but now make
+ // at least the first one work.
+ LdapCompressWhitespace(mDNSHost);
+
+ int32_t spacePos = mDNSHost.FindChar(' ');
+ // trim off trailing host(s)
+ if (spacePos != kNotFound)
+ mDNSHost.SetLength(spacePos);
+
+ rv = pDNSService->AsyncResolve(mDNSHost, 0, this, curThread,
+ getter_AddRefs(mDNSRequest));
+
+ if (NS_FAILED(rv)) {
+ switch (rv) {
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_FAILURE:
+ case NS_ERROR_OFFLINE:
+ break;
+
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ mDNSHost.Truncate();
+ }
+ return rv;
+}
+
+// this might get exposed to clients, so we've broken it
+// out of the destructor.
+void
+nsLDAPConnection::Close()
+{
+ int rc;
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbinding\n"));
+
+ if (mConnectionHandle) {
+ // note that the ldap_unbind() call in the 5.0 version of the LDAP C SDK
+ // appears to be exactly identical to ldap_unbind_s(), so it may in fact
+ // still be synchronous
+ //
+ rc = ldap_unbind(mConnectionHandle);
+#ifdef PR_LOGGING
+ if (rc != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPConnection::Close(): %s\n",
+ ldap_err2string(rc)));
+ }
+#endif
+ mConnectionHandle = nullptr;
+ }
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbound\n"));
+
+ NS_ASSERTION(!mThread || NS_SUCCEEDED(mThread->Shutdown()),
+ "Failed to shutdown thread cleanly");
+
+ // Cancel the DNS lookup if needed, and also drop the reference to the
+ // Init listener (if still there).
+ //
+ if (mDNSRequest) {
+ mDNSRequest->Cancel(NS_ERROR_ABORT);
+ mDNSRequest = nullptr;
+ }
+ mInitListener = nullptr;
+
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, "profile-change-net-teardown")) {
+ // Abort all ldap requests.
+ /* We cannot use enumerate function to abort operations because
+ * nsILDAPOperation::AbandonExt() is modifying list of operations
+ * and this leads to starvation.
+ * We have to do a copy of pending operations.
+ */
+ nsTArray<nsILDAPOperation*> pending_operations;
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ for (auto iter = mPendingOperations.Iter(); !iter.Done(); iter.Next()) {
+ pending_operations.AppendElement(iter.UserData());
+ }
+ }
+ for (uint32_t i = 0; i < pending_operations.Length(); i++) {
+ pending_operations[i]->AbandonExt();
+ }
+ Close();
+ } else {
+ NS_NOTREACHED("unexpected topic");
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::GetClosure(nsISupports **_retval)
+{
+ if (!_retval) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ NS_IF_ADDREF(*_retval = mClosure);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::SetClosure(nsISupports *aClosure)
+{
+ mClosure = aClosure;
+ return NS_OK;
+}
+
+// who we're binding as
+//
+// readonly attribute AUTF8String bindName
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetBindName(nsACString& _retval)
+{
+ _retval.Assign(mBindName);
+ return NS_OK;
+}
+
+// wrapper for ldap_get_lderrno
+// XXX should copy before returning
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetLdErrno(nsACString& matched, nsACString& errString,
+ int32_t *_retval)
+{
+ char *match, *err;
+
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = ldap_get_lderrno(mConnectionHandle, &match, &err);
+ matched.Assign(match);
+ errString.Assign(err);
+ return NS_OK;
+}
+
+// return the error string corresponding to GetLdErrno.
+//
+// XXX - deal with optional params
+// XXX - how does ldap_perror know to look at the global errno?
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetErrorString(char16_t **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // get the error string
+ //
+ char *rv = ldap_err2string(ldap_get_lderrno(mConnectionHandle, 0, 0));
+ if (!rv) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // make a copy using the XPCOM shared allocator
+ //
+ *_retval = ToNewUnicode(NS_ConvertUTF8toUTF16(rv));
+ if (!*_retval) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+/**
+ * Add an nsILDAPOperation to the list of operations pending on
+ * this connection. This is also mainly intended for use by the
+ * nsLDAPOperation code.
+ */
+nsresult
+nsLDAPConnection::AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation)
+{
+ NS_ENSURE_ARG_POINTER(aOperation);
+
+ nsIRunnable* runnable = new nsLDAPConnectionRunnable(aOperationID, aOperation,
+ this);
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Put((uint32_t)aOperationID, aOperation);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("pending operation added; total pending operations now = %d\n",
+ mPendingOperations.Count()));
+ }
+
+ nsresult rv;
+ if (!mThread)
+ {
+ rv = NS_NewThread(getter_AddRefs(mThread), runnable);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ rv = mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Remove an nsILDAPOperation from the list of operations pending on this
+ * connection. Mainly intended for use by the nsLDAPOperation code.
+ *
+ * @param aOperation operation to add
+ * @exception NS_ERROR_INVALID_POINTER aOperation was NULL
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ * @exception NS_ERROR_FAILURE could not delete the operation
+ *
+ * void removePendingOperation(in nsILDAPOperation aOperation);
+ */
+nsresult
+nsLDAPConnection::RemovePendingOperation(uint32_t aOperationID)
+{
+ NS_ENSURE_TRUE(aOperationID > 0, NS_ERROR_UNEXPECTED);
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPConnection::RemovePendingOperation(): operation removed\n"));
+
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Remove(aOperationID);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPConnection::RemovePendingOperation(): operation "
+ "removed; total pending operations now = %d\n",
+ mPendingOperations.Count()));
+ }
+
+ return NS_OK;
+}
+
+class nsOnLDAPMessageRunnable : public Runnable
+{
+public:
+ nsOnLDAPMessageRunnable(nsLDAPMessage *aMsg, bool aClear)
+ : m_msg(aMsg)
+ , m_clear(aClear)
+ {}
+ NS_DECL_NSIRUNNABLE
+private:
+ RefPtr<nsLDAPMessage> m_msg;
+ bool m_clear;
+};
+
+NS_IMETHODIMP nsOnLDAPMessageRunnable::Run()
+{
+ // get the message listener object.
+ nsLDAPOperation *nsoperation = static_cast<nsLDAPOperation *>(m_msg->mOperation.get());
+ nsCOMPtr<nsILDAPMessageListener> listener;
+ nsresult rv = nsoperation->GetMessageListener(getter_AddRefs(listener));
+
+ if (m_clear)
+ {
+ // try to break cycles
+ nsoperation->Clear();
+ }
+
+ if (!listener)
+ {
+ NS_ERROR("nsLDAPConnection::InvokeMessageCallback(): probable "
+ "memory corruption: GetMessageListener() returned nullptr");
+ return rv;
+ }
+
+ return listener->OnLDAPMessage(m_msg);
+}
+
+nsresult
+nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle,
+ nsILDAPMessage *aMsg,
+ int32_t aOperation,
+ bool aRemoveOpFromConnQ)
+{
+#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, ("InvokeMessageCallback entered\n"));
+#endif
+
+ // Get the operation.
+ nsCOMPtr<nsILDAPOperation> operation;
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Get((uint32_t)aOperation, getter_AddRefs(operation));
+ }
+
+ NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER);
+
+ nsLDAPMessage *msg = static_cast<nsLDAPMessage *>(aMsg);
+ msg->mOperation = operation;
+
+ // proxy the listener callback to the ui thread.
+ RefPtr<nsOnLDAPMessageRunnable> runnable =
+ new nsOnLDAPMessageRunnable(msg, aRemoveOpFromConnQ);
+ // invoke the callback
+ NS_DispatchToMainThread(runnable);
+
+ // if requested (ie the operation is done), remove the operation
+ // from the connection queue.
+ if (aRemoveOpFromConnQ)
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Remove(aOperation);
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("pending operation removed; total pending operations now ="
+ " %d\n", mPendingOperations.Count()));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::OnLookupComplete(nsICancelable *aRequest,
+ nsIDNSRecord *aRecord,
+ nsresult aStatus)
+{
+ nsresult rv = NS_OK;
+
+ if (aRecord) {
+ // Build mResolvedIP list
+ //
+ mResolvedIP.Truncate();
+
+ int32_t index = 0;
+ nsCString addrbuf;
+ nsCOMPtr<nsINetAddr> addr;
+
+ while (NS_SUCCEEDED(aRecord->GetScriptableNextAddr(0, getter_AddRefs(addr)))) {
+ // We can only use v4 addresses
+ //
+ uint16_t family = 0;
+ bool v4mapped = false;
+ addr->GetFamily(&family);
+ addr->GetIsV4Mapped(&v4mapped);
+ if (family == nsINetAddr::FAMILY_INET || v4mapped) {
+ // If there are more IPs in the list, we separate them with
+ // a space, as supported/used by the LDAP C-SDK.
+ //
+ if (index++)
+ mResolvedIP.Append(' ');
+
+ // Convert the IPv4 address to a string, and append it to our
+ // list of IPs. Strip leading '::FFFF:' (the IPv4-mapped-IPv6
+ // indicator) if present.
+ //
+ addr->GetAddress(addrbuf);
+ if (addrbuf[0] == ':' && addrbuf.Length() > 7)
+ mResolvedIP.Append(Substring(addrbuf, 7));
+ else
+ mResolvedIP.Append(addrbuf);
+ }
+ }
+ }
+
+ if (NS_FAILED(aStatus)) {
+ // The DNS service failed, lets pass something reasonable
+ // back to the listener.
+ //
+ switch (aStatus) {
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_FAILURE:
+ case NS_ERROR_OFFLINE:
+ rv = aStatus;
+ break;
+
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+ } else if (!mResolvedIP.Length()) {
+ // We have no host resolved, that is very bad, and should most
+ // likely have been caught earlier.
+ //
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): the resolved IP "
+ "string is empty.\n");
+
+ rv = NS_ERROR_UNKNOWN_HOST;
+ } else {
+ // We've got the IP(s) for the hostname, now lets setup the
+ // LDAP connection using this information. Note that if the
+ // LDAP server returns a referral, the C-SDK will perform a
+ // new, synchronous DNS lookup, which might hang (but hopefully
+ // if we've come this far, DNS is working properly).
+ //
+ mConnectionHandle = ldap_init(mResolvedIP.get(),
+ mPort == -1 ?
+ (mSSL ? LDAPS_PORT : LDAP_PORT) : mPort);
+ // Check that we got a proper connection, and if so, setup the
+ // threading functions for this connection.
+ //
+ if ( !mConnectionHandle ) {
+ rv = NS_ERROR_FAILURE; // LDAP C SDK API gives no useful error
+ } else {
+#if defined(DEBUG_dmose) || defined(DEBUG_bienvenu)
+ const int lDebug = 0;
+ ldap_set_option(mConnectionHandle, LDAP_OPT_DEBUG_LEVEL, &lDebug);
+#endif
+
+ // the C SDK currently defaults to v2. if we're to use v3,
+ // tell it so.
+ //
+ int version;
+ switch (mVersion) {
+ case 2:
+ break;
+ case 3:
+ version = LDAP_VERSION3;
+ ldap_set_option(mConnectionHandle, LDAP_OPT_PROTOCOL_VERSION,
+ &version);
+ break;
+ default:
+ NS_ERROR("nsLDAPConnection::OnLookupComplete(): mVersion"
+ " invalid");
+ }
+
+ // This code sets up the current connection to use PSM for SSL
+ // functionality. Making this use libssldap instead for
+ // non-browser user shouldn't be hard.
+
+ extern nsresult nsLDAPInstallSSL(LDAP *ld, const char *aHostName);
+
+ if (mSSL) {
+ if (ldap_set_option(mConnectionHandle, LDAP_OPT_SSL,
+ LDAP_OPT_ON) != LDAP_SUCCESS ) {
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): Error"
+ " configuring connection to use SSL");
+ rv = NS_ERROR_UNEXPECTED;
+ }
+
+ rv = nsLDAPInstallSSL(mConnectionHandle, mDNSHost.get());
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): Error"
+ " installing secure LDAP routines for"
+ " connection");
+ }
+ }
+ }
+ }
+
+ // Drop the DNS request object, we no longer need it, and set the flag
+ // indicating that DNS has finished.
+ //
+ mDNSRequest = nullptr;
+ mDNSHost.Truncate();
+
+ // Call the listener, and then we can release our reference to it.
+ //
+ mInitListener->OnLDAPInit(this, rv);
+ mInitListener = nullptr;
+
+ return rv;
+}
+
+nsLDAPConnectionRunnable::nsLDAPConnectionRunnable(int32_t aOperationID,
+ nsILDAPOperation *aOperation,
+ nsLDAPConnection *aConnection)
+ : mOperationID(aOperationID), mConnection(aConnection)
+{
+}
+
+nsLDAPConnectionRunnable::~nsLDAPConnectionRunnable()
+{
+ if (mConnection) {
+ NS_ReleaseOnMainThread(mConnection.forget());
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsLDAPConnectionRunnable, nsIRunnable)
+
+NS_IMETHODIMP nsLDAPConnectionRunnable::Run()
+{
+ if (!mOperationID) {
+ NS_ERROR("mOperationID is null");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ LDAPMessage *msgHandle;
+ bool operationFinished = true;
+ RefPtr<nsLDAPMessage> msg;
+
+ struct timeval timeout = { 0, 0 };
+
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ int32_t returnCode = ldap_result(mConnection->mConnectionHandle, mOperationID, LDAP_MSG_ONE, &timeout, &msgHandle);
+ switch (returnCode)
+ {
+ // timeout
+ case 0:
+ // XXX do we need a timer?
+ return thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ case -1:
+ NS_ERROR("We don't know what went wrong with the ldap operation");
+ return NS_ERROR_FAILURE;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ // XXX what should we do with LDAP_RES_SEARCH_EXTENDED
+ operationFinished = false;
+ MOZ_FALLTHROUGH;
+ default:
+ {
+ msg = new nsLDAPMessage;
+ if (!msg)
+ return NS_ERROR_NULL_POINTER;
+
+ // initialize the message, using a protected method not available
+ // through nsILDAPMessage (which is why we need the raw pointer)
+ nsresult rv = msg->Init(mConnection, msgHandle);
+
+ switch (rv)
+ {
+ case NS_OK:
+ {
+ int32_t errorCode;
+ msg->GetErrorCode(&errorCode);
+
+ // maybe a version error, e.g., using v3 on a v2 server.
+ // if we're using v3, try v2.
+ if (errorCode == LDAP_PROTOCOL_ERROR &&
+ mConnection->mVersion == nsILDAPConnection::VERSION3)
+ {
+ nsAutoCString password;
+ mConnection->mVersion = nsILDAPConnection::VERSION2;
+ ldap_set_option(mConnection->mConnectionHandle,
+ LDAP_OPT_PROTOCOL_VERSION, &mConnection->mVersion);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // We don't want to notify callers that we are done, so
+ // redispatch the runnable.
+ // XXX do we need a timer?
+ rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+ }
+ }
+ // If we're midway through a SASL Bind, we need to continue
+ // without letting our caller know what we're up to!
+ //
+ if (errorCode == LDAP_SASL_BIND_IN_PROGRESS) {
+ struct berval *creds;
+ ldap_parse_sasl_bind_result(
+ mConnection->mConnectionHandle, msgHandle,
+ &creds, 0);
+
+ nsCOMPtr<nsILDAPOperation> operation;
+ {
+ MutexAutoLock lock(mConnection->mPendingOperationsMutex);
+ mConnection->mPendingOperations.Get((uint32_t)mOperationID, getter_AddRefs(operation));
+ }
+
+ NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER);
+
+ nsresult rv = operation->SaslStep(creds->bv_val, creds->bv_len);
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+ }
+ break;
+ }
+ // Error code handling in here
+ default:
+ return NS_OK;
+ }
+
+ // invoke the callback on the nsILDAPOperation corresponding to
+ // this message
+ rv = mConnection->InvokeMessageCallback(msgHandle, msg, mOperationID,
+ operationFinished);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("CheckLDAPOperationResult(): error invoking message"
+ " callback");
+ // punt and hope things work out better next time around
+ return NS_OK;
+ }
+
+ if (!operationFinished)
+ {
+ // XXX do we need a timer?
+ rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ break;
+ }
+ }
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPConnection.h b/ldap/xpcom/src/nsLDAPConnection.h
new file mode 100644
index 000000000..08c88295c
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPConnection.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#ifndef _nsLDAPConnection_h_
+#define _nsLDAPConnection_h_
+
+#include "nsILDAPConnection.h"
+#include "ldap.h"
+#include "nsStringGlue.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPMessageListener.h"
+#include "nsInterfaceHashtable.h"
+#include "nspr.h"
+#include "nsWeakReference.h"
+#include "nsWeakPtr.h"
+#include "nsIDNSListener.h"
+#include "nsICancelable.h"
+#include "nsIRequest.h"
+#include "nsCOMArray.h"
+#include "nsIObserver.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Mutex.h"
+
+/**
+ * Casting nsILDAPConnection to nsISupports is ambiguous.
+ * This method handles that.
+ */
+inline nsISupports*
+ToSupports(nsILDAPConnection* p)
+{
+ return NS_ISUPPORTS_CAST(nsILDAPConnection*, p);
+}
+
+// 0d871e30-1dd2-11b2-8ea9-831778c78e93
+//
+#define NS_LDAPCONNECTION_CID \
+{ 0x0d871e30, 0x1dd2, 0x11b2, \
+ { 0x8e, 0xa9, 0x83, 0x17, 0x78, 0xc7, 0x8e, 0x93 }}
+
+class nsLDAPConnection : public nsILDAPConnection,
+ public nsSupportsWeakReference,
+ public nsIDNSListener,
+ public nsIObserver
+
+{
+ friend class nsLDAPOperation;
+ friend class nsLDAPMessage;
+ friend class nsLDAPConnectionRunnable;
+ typedef mozilla::Mutex Mutex;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPCONNECTION
+ NS_DECL_NSIDNSLISTENER
+ NS_DECL_NSIOBSERVER
+
+ // constructor & destructor
+ //
+ nsLDAPConnection();
+
+ protected:
+ virtual ~nsLDAPConnection();
+ // invoke the callback associated with a given message, and possibly
+ // delete it from the connection queue
+ //
+ nsresult InvokeMessageCallback(LDAPMessage *aMsgHandle,
+ nsILDAPMessage *aMsg,
+ int32_t aOperation,
+ bool aRemoveOpFromConnQ);
+ /**
+ * Add an nsILDAPOperation to the list of operations pending on
+ * this connection. This is mainly intended for use by the
+ * nsLDAPOperation code. Used so that the thread waiting on messages
+ * for this connection has an operation to callback to.
+ *
+ * @param aOperation operation to add
+ * @exception NS_ERROR_ILLEGAL_VALUE aOperation was NULL
+ * @exception NS_ERROR_UNEXPECTED this operation's msgId was not
+ * unique to this connection
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ */
+ nsresult AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation);
+
+ /**
+ * Remove an nsILDAPOperation from the list of operations pending on this
+ * connection. Mainly intended for use by the nsLDAPOperation code.
+ *
+ * @param aOperation operation to add
+ * @exception NS_ERROR_INVALID_POINTER aOperation was NULL
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ * @exception NS_ERROR_FAILURE could not delete the operation
+ */
+ nsresult RemovePendingOperation(uint32_t aOperationID);
+
+ void Close(); // close the connection
+ LDAP *mConnectionHandle; // the LDAP C SDK's connection object
+ nsCString mBindName; // who to bind as
+ nsCOMPtr<nsIThread> mThread; // thread which marshals results
+
+ Mutex mPendingOperationsMutex;
+ nsInterfaceHashtable<nsUint32HashKey, nsILDAPOperation> mPendingOperations;
+
+ int32_t mPort; // The LDAP port we're binding to
+ bool mSSL; // the options
+ uint32_t mVersion; // LDAP protocol version
+
+ nsCString mResolvedIP; // Preresolved list of host IPs
+ nsCOMPtr<nsILDAPMessageListener> mInitListener; // Init callback
+ nsCOMPtr<nsICancelable> mDNSRequest; // The "active" DNS request
+ nsCString mDNSHost; // The hostname being resolved
+ nsCOMPtr<nsISupports> mClosure; // private parameter (anything caller desires)
+};
+
+class nsLDAPConnectionRunnable : public nsIRunnable
+{
+ friend class nsLDAPConnection;
+ friend class nsLDAPMessage;
+
+public:
+ nsLDAPConnectionRunnable(int32_t aOperationID,
+ nsILDAPOperation *aOperation,
+ nsLDAPConnection *aConnection);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ int32_t mOperationID;
+ RefPtr<nsLDAPConnection> mConnection;
+
+private:
+ virtual ~nsLDAPConnectionRunnable();
+};
+
+#endif // _nsLDAPConnection_h_
diff --git a/ldap/xpcom/src/nsLDAPControl.cpp b/ldap/xpcom/src/nsLDAPControl.cpp
new file mode 100644
index 000000000..a01972835
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPControl.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsLDAPControl.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsLDAPBERValue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPControl, nsILDAPControl)
+
+nsLDAPControl::nsLDAPControl()
+ : mIsCritical(false)
+{
+}
+
+nsLDAPControl::~nsLDAPControl()
+{
+}
+
+/* attribute ACString oid; */
+NS_IMETHODIMP nsLDAPControl::GetOid(nsACString & aOid)
+{
+ aOid.Assign(mOid);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPControl::SetOid(const nsACString & aOid)
+{
+ mOid = aOid;
+ return NS_OK;
+}
+
+/* attribute nsILDAPBERValue value; */
+NS_IMETHODIMP
+nsLDAPControl::GetValue(nsILDAPBERValue * *aValue)
+{
+ NS_IF_ADDREF(*aValue = mValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPControl::SetValue(nsILDAPBERValue * aValue)
+{
+ mValue = aValue;
+ return NS_OK;
+}
+
+/* attribute boolean isCritical; */
+NS_IMETHODIMP
+nsLDAPControl::GetIsCritical(bool *aIsCritical)
+{
+ *aIsCritical = mIsCritical;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLDAPControl::SetIsCritical(bool aIsCritical)
+{
+ mIsCritical = aIsCritical;
+ return NS_OK;
+}
+
+/**
+ * utility routine for use inside the LDAP XPCOM SDK
+ */
+nsresult
+nsLDAPControl::ToLDAPControl(LDAPControl **control)
+{
+ // because nsLDAPProtocolModule::Init calls prldap_install_routines we know
+ // that the C SDK will be using the NSPR allocator under the hood, so our
+ // callers will therefore be able to use ldap_control_free() and friends on
+ // this control.
+ LDAPControl *ctl = static_cast<LDAPControl *>(PR_Calloc(1, sizeof(LDAPControl)));
+ if (!ctl) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // need to ensure that this string is also alloced by PR_Alloc
+ ctl->ldctl_oid = PL_strdup(mOid.get());
+ if (!ctl->ldctl_oid) {
+ PR_Free(ctl);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ ctl->ldctl_iscritical = mIsCritical;
+
+ if (!mValue) {
+ // no data associated with this control
+ ctl->ldctl_value.bv_len = 0;
+ ctl->ldctl_value.bv_val = 0;
+ } else {
+
+ // just to make the code below a bit more readable
+ nsLDAPBERValue *nsBerVal =
+ static_cast<nsLDAPBERValue *>(static_cast<nsILDAPBERValue *>
+ (mValue.get()));
+ ctl->ldctl_value.bv_len = nsBerVal->mSize;
+
+ if (!nsBerVal->mSize) {
+ // a zero-length value is associated with this control
+ return NS_ERROR_NOT_IMPLEMENTED;
+ } else {
+
+ // same for the berval itself
+ ctl->ldctl_value.bv_len = nsBerVal->mSize;
+ ctl->ldctl_value.bv_val = static_cast<char *>
+ (PR_Malloc(nsBerVal->mSize));
+ if (!ctl->ldctl_value.bv_val) {
+ ldap_control_free(ctl);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ memcpy(ctl->ldctl_value.bv_val, nsBerVal->mValue,
+ ctl->ldctl_value.bv_len);
+ }
+ }
+
+ *control = ctl;
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPControl.h b/ldap/xpcom/src/nsLDAPControl.h
new file mode 100644
index 000000000..6baf269c7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPControl.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsILDAPControl.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPBERValue.h"
+#include "nsStringGlue.h"
+#include "ldap.h"
+
+// {5B608BBE-C0EA-4f74-B209-9CDCD79EC401}
+#define NS_LDAPCONTROL_CID \
+ { 0x5b608bbe, 0xc0ea, 0x4f74, \
+ { 0xb2, 0x9, 0x9c, 0xdc, 0xd7, 0x9e, 0xc4, 0x1 } }
+
+class nsLDAPControl final : public nsILDAPControl
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILDAPCONTROL
+
+ nsLDAPControl();
+
+ /**
+ * return a pointer to C-SDK compatible LDAPControl structure. Note that
+ * this is allocated with NS_Alloc and must be freed with NS_Free, both by
+ * ldap_control_free() and friends.
+ *
+ * @exception null pointer return if allocation failed
+ */
+ nsresult ToLDAPControl(LDAPControl **aControl);
+
+private:
+ ~nsLDAPControl();
+
+protected:
+ nsCOMPtr<nsILDAPBERValue> mValue; // the value portion of this control
+ bool mIsCritical; // should server abort if control not understood?
+ nsCString mOid; // Object ID for this control
+};
diff --git a/ldap/xpcom/src/nsLDAPInternal.h b/ldap/xpcom/src/nsLDAPInternal.h
new file mode 100644
index 000000000..989f113b3
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPInternal.h
@@ -0,0 +1,11 @@
+/* -*- 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 "prlog.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo *gLDAPLogModule; // defn in nsLDAPProtocolModule.cpp
+#endif
diff --git a/ldap/xpcom/src/nsLDAPMessage.cpp b/ldap/xpcom/src/nsLDAPMessage.cpp
new file mode 100644
index 000000000..782fa047f
--- /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;
+}
diff --git a/ldap/xpcom/src/nsLDAPMessage.h b/ldap/xpcom/src/nsLDAPMessage.h
new file mode 100644
index 000000000..d3c0b2d2e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPMessage.h
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#ifndef _nsLDAPMessage_h_
+#define _nsLDAPMessage_h_
+
+#include "ldap.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPOperation.h"
+#include "nsCOMPtr.h"
+
+// 76e061ad-a59f-43b6-b812-ee6e8e69423f
+//
+#define NS_LDAPMESSAGE_CID \
+{ 0x76e061ad, 0xa59f, 0x43b6, \
+ { 0xb8, 0x12, 0xee, 0x6e, 0x8e, 0x69, 0x42, 0x3f }}
+
+class nsLDAPMessage : public nsILDAPMessage
+{
+ friend class nsLDAPOperation;
+ friend class nsLDAPConnection;
+ friend class nsLDAPConnectionRunnable;
+ friend class nsOnLDAPMessageRunnable;
+
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMESSAGE
+
+ // constructor & destructor
+ //
+ nsLDAPMessage();
+
+ protected:
+ virtual ~nsLDAPMessage();
+
+ nsresult IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount,
+ char** *aAttributes, BerElement *position);
+ nsresult IterateAttributes(uint32_t *aAttrCount, char** *aAttributes,
+ bool getP);
+ nsresult Init(nsILDAPConnection *aConnection,
+ LDAPMessage *aMsgHandle);
+ LDAPMessage *mMsgHandle; // the message we're wrapping
+ nsCOMPtr<nsILDAPOperation> mOperation; // operation this msg relates to
+
+ LDAP *mConnectionHandle; // cached connection this op is on
+
+ // since we're caching the connection handle (above), we need to
+ // hold an owning ref to the relevant nsLDAPConnection object as long
+ // as we're around
+ //
+ nsCOMPtr<nsILDAPConnection> mConnection;
+
+ // the next five member vars are returned by ldap_parse_result()
+ //
+ int mErrorCode;
+ char *mMatchedDn;
+ char *mErrorMessage;
+ char **mReferrals;
+ LDAPControl **mServerControls;
+};
+
+#endif // _nsLDAPMessage_h
diff --git a/ldap/xpcom/src/nsLDAPModification.cpp b/ldap/xpcom/src/nsLDAPModification.cpp
new file mode 100644
index 000000000..77253835a
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPModification.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsLDAPModification.h"
+#include "nsILDAPBERValue.h"
+#include "nsISimpleEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsLDAPModification, nsILDAPModification)
+
+// constructor
+//
+nsLDAPModification::nsLDAPModification()
+ : mValuesLock("nsLDAPModification.mValuesLock")
+{
+}
+
+// destructor
+//
+nsLDAPModification::~nsLDAPModification()
+{
+}
+
+nsresult
+nsLDAPModification::Init()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetOperation(int32_t *aOperation)
+{
+ NS_ENSURE_ARG_POINTER(aOperation);
+
+ *aOperation = mOperation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPModification::SetOperation(int32_t aOperation)
+{
+ mOperation = aOperation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetType(nsACString& aType)
+{
+ aType.Assign(mType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetType(const nsACString& aType)
+{
+ mType.Assign(aType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetValues(nsIArray** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ MutexAutoLock lock(mValuesLock);
+
+ if (!mValues)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_ADDREF(*aResult = mValues);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetValues(nsIArray* aValues)
+{
+ NS_ENSURE_ARG_POINTER(aValues);
+
+ MutexAutoLock lock(mValuesLock);
+ nsresult rv;
+
+ if (!mValues)
+ mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = mValues->Clear();
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aValues->Enumerate(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMoreElements;
+ rv = enumerator->HasMoreElements(&hasMoreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> value;
+
+ while (hasMoreElements)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mValues->AppendElement(value, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = enumerator->HasMoreElements(&hasMoreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetUpModification(int32_t aOperation,
+ const nsACString &aType,
+ nsIArray *aValues)
+{
+ // Set the values using our local function before entering lock
+ // to avoid deadlocks due to holding the same lock twice.
+ nsresult rv = SetValues(aValues);
+
+ MutexAutoLock lock(mValuesLock);
+
+ mOperation = aOperation;
+ mType.Assign(aType);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetUpModificationOneValue(int32_t aOperation,
+ const nsACString &aType,
+ nsILDAPBERValue *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ MutexAutoLock lock(mValuesLock);
+
+ mOperation = aOperation;
+ mType.Assign(aType);
+
+ nsresult rv;
+
+ if (!mValues)
+ mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = mValues->Clear();
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mValues->AppendElement(aValue, false);
+}
diff --git a/ldap/xpcom/src/nsLDAPModification.h b/ldap/xpcom/src/nsLDAPModification.h
new file mode 100644
index 000000000..e9c9c5599
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPModification.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef _nsLDAPModification_h_
+#define _nsLDAPModification_h_
+
+#include "nsILDAPModification.h"
+#include "nsIMutableArray.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Mutex.h"
+
+// 5b0f4d00-062e-11d6-a7f2-fc943c3c039c
+//
+#define NS_LDAPMODIFICATION_CID \
+{ 0x5b0f4d00, 0x062e, 0x11d6, \
+ { 0xa7, 0xf2, 0xfc, 0x94, 0x3c, 0x3c, 0x03, 0x9c }}
+
+class nsLDAPModification : public nsILDAPModification
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMODIFICATION
+
+ // constructor & destructor
+ //
+ nsLDAPModification();
+
+ nsresult Init();
+
+private:
+ virtual ~nsLDAPModification();
+
+ int32_t mOperation;
+ nsCString mType;
+ nsCOMPtr<nsIMutableArray> mValues;
+ mozilla::Mutex mValuesLock;
+};
+
+#endif // _nsLDAPModification_h_
diff --git a/ldap/xpcom/src/nsLDAPOperation.cpp b/ldap/xpcom/src/nsLDAPOperation.cpp
new file mode 100644
index 000000000..72cf32089
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPOperation.cpp
@@ -0,0 +1,975 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsLDAPOperation.h"
+#include "nsLDAPBERValue.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPModification.h"
+#include "nsIComponentManager.h"
+#include "nspr.h"
+#include "nsISimpleEnumerator.h"
+#include "nsLDAPControl.h"
+#include "nsILDAPErrors.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIAuthModule.h"
+#include "nsArrayUtils.h"
+#include "nsMemory.h"
+#include "mozilla/Logging.h"
+
+// Helper function
+static nsresult TranslateLDAPErrorToNSError(const int ldapError)
+{
+ switch (ldapError) {
+ case LDAP_SUCCESS:
+ return NS_OK;
+
+ case LDAP_ENCODING_ERROR:
+ return NS_ERROR_LDAP_ENCODING_ERROR;
+
+ case LDAP_CONNECT_ERROR:
+ return NS_ERROR_LDAP_CONNECT_ERROR;
+
+ case LDAP_SERVER_DOWN:
+ return NS_ERROR_LDAP_SERVER_DOWN;
+
+ case LDAP_NO_MEMORY:
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ case LDAP_NOT_SUPPORTED:
+ return NS_ERROR_LDAP_NOT_SUPPORTED;
+
+ case LDAP_PARAM_ERROR:
+ return NS_ERROR_INVALID_ARG;
+
+ case LDAP_FILTER_ERROR:
+ return NS_ERROR_LDAP_FILTER_ERROR;
+
+ default:
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("TranslateLDAPErrorToNSError: "
+ "Do not know how to translate LDAP error: 0x%x", ldapError));
+ return NS_ERROR_UNEXPECTED;
+ }
+}
+
+
+// constructor
+nsLDAPOperation::nsLDAPOperation()
+{
+}
+
+// destructor
+nsLDAPOperation::~nsLDAPOperation()
+{
+}
+
+NS_IMPL_CLASSINFO(nsLDAPOperation, NULL, nsIClassInfo::THREADSAFE,
+ NS_LDAPOPERATION_CID)
+
+NS_IMPL_ADDREF(nsLDAPOperation)
+NS_IMPL_RELEASE(nsLDAPOperation)
+NS_INTERFACE_MAP_BEGIN(nsLDAPOperation)
+ NS_INTERFACE_MAP_ENTRY(nsILDAPOperation)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPOperation)
+ NS_IMPL_QUERY_CLASSINFO(nsLDAPOperation)
+NS_INTERFACE_MAP_END_THREADSAFE
+NS_IMPL_CI_INTERFACE_GETTER(nsLDAPOperation, nsILDAPOperation)
+
+/**
+ * Initializes this operation. Must be called prior to use.
+ *
+ * @param aConnection connection this operation should use
+ * @param aMessageListener where are the results are called back to.
+ */
+NS_IMETHODIMP
+nsLDAPOperation::Init(nsILDAPConnection *aConnection,
+ nsILDAPMessageListener *aMessageListener,
+ nsISupports *aClosure)
+{
+ if (!aConnection) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // so we know that the operation is not yet running (and therefore don't
+ // try and call ldap_abandon_ext() on it) or remove it from the queue.
+ //
+ mMsgID = 0;
+
+ // set the member vars
+ //
+ mConnection = static_cast<nsLDAPConnection*>(aConnection);
+ mMessageListener = aMessageListener;
+ mClosure = aClosure;
+
+ // cache the connection handle
+ //
+ mConnectionHandle =
+ mConnection->mConnectionHandle;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::GetClosure(nsISupports **_retval)
+{
+ if (!_retval) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ NS_IF_ADDREF(*_retval = mClosure);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SetClosure(nsISupports *aClosure)
+{
+ mClosure = aClosure;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::GetConnection(nsILDAPConnection* *aConnection)
+{
+ if (!aConnection) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ *aConnection = mConnection;
+ NS_IF_ADDREF(*aConnection);
+
+ return NS_OK;
+}
+
+void
+nsLDAPOperation::Clear()
+{
+ mMessageListener = nullptr;
+ mClosure = nullptr;
+ mConnection = nullptr;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::GetMessageListener(nsILDAPMessageListener **aMessageListener)
+{
+ if (!aMessageListener) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ *aMessageListener = mMessageListener;
+ NS_IF_ADDREF(*aMessageListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SaslBind(const nsACString &service,
+ const nsACString &mechanism,
+ nsIAuthModule *authModule)
+{
+ nsresult rv;
+ nsAutoCString bindName;
+ struct berval creds;
+ unsigned int credlen;
+
+ mAuthModule = authModule;
+ mMechanism.Assign(mechanism);
+
+ rv = mConnection->GetBindName(bindName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ creds.bv_val = NULL;
+ mAuthModule->Init(PromiseFlatCString(service).get(),
+ nsIAuthModule::REQ_DEFAULT, nullptr,
+ NS_ConvertUTF8toUTF16(bindName).get(), nullptr);
+
+ rv = mAuthModule->GetNextToken(nullptr, 0, (void **)&creds.bv_val,
+ &credlen);
+ if (NS_FAILED(rv) || !creds.bv_val)
+ return rv;
+
+ creds.bv_len = credlen;
+ const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(),
+ mMechanism.get(), &creds, NULL, NULL,
+ &mMsgID);
+ free(creds.bv_val);
+
+ if (lderrno != LDAP_SUCCESS)
+ return TranslateLDAPErrorToNSError(lderrno);
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+
+ if (NS_FAILED(rv))
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SaslStep(const char *token, uint32_t tokenLen)
+{
+ nsresult rv;
+ nsAutoCString bindName;
+ struct berval clientCreds;
+ struct berval serverCreds;
+ unsigned int credlen;
+
+ rv = mConnection->RemovePendingOperation(mMsgID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ serverCreds.bv_val = (char *) token;
+ serverCreds.bv_len = tokenLen;
+
+ rv = mConnection->GetBindName(bindName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mAuthModule->GetNextToken(serverCreds.bv_val, serverCreds.bv_len,
+ (void **) &clientCreds.bv_val, &credlen);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ clientCreds.bv_len = credlen;
+
+ const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(),
+ mMechanism.get(), &clientCreds, NULL,
+ NULL, &mMsgID);
+
+ free(clientCreds.bv_val);
+
+ if (lderrno != LDAP_SUCCESS)
+ return TranslateLDAPErrorToNSError(lderrno);
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+ if (NS_FAILED(rv))
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+
+ return rv;
+}
+
+
+// wrapper for ldap_simple_bind()
+//
+NS_IMETHODIMP
+nsLDAPOperation::SimpleBind(const nsACString& passwd)
+{
+ RefPtr<nsLDAPConnection> connection = mConnection;
+ // There is a possibilty that mConnection can be cleared by another
+ // thread. Grabbing a local reference to mConnection may avoid this.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=557928#c1
+ nsresult rv;
+ nsAutoCString bindName;
+ int32_t originalMsgID = mMsgID;
+ // Ugly hack alert:
+ // the first time we get called with a passwd, remember it.
+ // Then, if we get called again w/o a password, use the
+ // saved one. Getting called again means we're trying to
+ // fall back to VERSION2.
+ // Since LDAP operations are thrown away when done, it won't stay
+ // around in memory.
+ if (!passwd.IsEmpty())
+ mSavePassword = passwd;
+
+ NS_PRECONDITION(mMessageListener, "MessageListener not set");
+
+ rv = connection->GetBindName(bindName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::SimpleBind(): called; bindName = '%s'; ",
+ bindName.get()));
+
+ // this (nsLDAPOperation) may be released by RemovePendingOperation()
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1063829.
+ RefPtr<nsLDAPOperation> kungFuDeathGrip = this;
+
+ // If this is a second try at binding, remove the operation from pending ops
+ // because msg id has changed...
+ if (originalMsgID)
+ connection->RemovePendingOperation(originalMsgID);
+
+ mMsgID = ldap_simple_bind(mConnectionHandle, bindName.get(),
+ mSavePassword.get());
+
+ if (mMsgID == -1) {
+ // XXX Should NS_ERROR_LDAP_SERVER_DOWN cause a rebind here?
+ return TranslateLDAPErrorToNSError(ldap_get_lderrno(mConnectionHandle,
+ 0, 0));
+ }
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = connection->AddPendingOperation(mMsgID, this);
+ switch (rv) {
+ case NS_OK:
+ break;
+
+ // note that the return value of ldap_abandon_ext() is ignored, as
+ // there's nothing useful to do with it
+
+ case NS_ERROR_OUT_OF_MEMORY:
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ return NS_ERROR_OUT_OF_MEMORY;
+ break;
+
+ case NS_ERROR_UNEXPECTED:
+ case NS_ERROR_ILLEGAL_VALUE:
+ default:
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Given an nsIArray of nsILDAPControls, return the appropriate
+ * zero-terminated array of LDAPControls ready to pass in to the C SDK.
+ */
+static nsresult
+convertControlArray(nsIArray *aXpcomArray, LDAPControl ***aArray)
+{
+ // get the size of the original array
+ uint32_t length;
+ nsresult rv = aXpcomArray->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // don't allocate an array if someone passed us in an empty one
+ if (!length) {
+ *aArray = 0;
+ return NS_OK;
+ }
+
+ // allocate a local array of the form understood by the C-SDK;
+ // +1 is to account for the final null terminator. PR_Calloc is
+ // is used so that ldap_controls_free will work anywhere during the
+ // iteration
+ LDAPControl **controls =
+ static_cast<LDAPControl **>
+ (PR_Calloc(length+1, sizeof(LDAPControl)));
+
+ // prepare to enumerate the array
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aXpcomArray->Enumerate(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool moreElements;
+ rv = enumerator->HasMoreElements(&moreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t i = 0;
+ while (moreElements) {
+
+ // get the next array element
+ nsCOMPtr<nsISupports> isupports;
+ rv = enumerator->GetNext(getter_AddRefs(isupports));
+ if (NS_FAILED(rv)) {
+ ldap_controls_free(controls);
+ return rv;
+ }
+ nsCOMPtr<nsILDAPControl> control = do_QueryInterface(isupports, &rv);
+ if (NS_FAILED(rv)) {
+ ldap_controls_free(controls);
+ return NS_ERROR_INVALID_ARG; // bogus element in the array
+ }
+ nsLDAPControl *ctl = static_cast<nsLDAPControl *>
+ (static_cast<nsILDAPControl *>
+ (control.get()));
+
+ // convert it to an LDAPControl structure placed in the new array
+ rv = ctl->ToLDAPControl(&controls[i]);
+ if (NS_FAILED(rv)) {
+ ldap_controls_free(controls);
+ return rv;
+ }
+
+ // on to the next element
+ rv = enumerator->HasMoreElements(&moreElements);
+ if (NS_FAILED(rv)) {
+ ldap_controls_free(controls);
+ return NS_ERROR_UNEXPECTED;
+ }
+ ++i;
+ }
+
+ *aArray = controls;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SearchExt(const nsACString& aBaseDn, int32_t aScope,
+ const nsACString& aFilter,
+ const nsACString &aAttributes,
+ PRIntervalTime aTimeOut, int32_t aSizeLimit)
+{
+ if (!mMessageListener) {
+ NS_ERROR("nsLDAPOperation::SearchExt(): mMessageListener not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // XXX add control logging
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::SearchExt(): called with aBaseDn = '%s'; "
+ "aFilter = '%s'; aAttributes = %s; aSizeLimit = %d",
+ PromiseFlatCString(aBaseDn).get(),
+ PromiseFlatCString(aFilter).get(),
+ PromiseFlatCString(aAttributes).get(), aSizeLimit));
+
+ LDAPControl **serverctls = 0;
+ nsresult rv;
+ if (mServerControls) {
+ rv = convertControlArray(mServerControls, &serverctls);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPOperation::SearchExt(): error converting server "
+ "control array: %x", rv));
+ return rv;
+ }
+ }
+
+ LDAPControl **clientctls = 0;
+ if (mClientControls) {
+ rv = convertControlArray(mClientControls, &clientctls);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPOperation::SearchExt(): error converting client "
+ "control array: %x", rv));
+ ldap_controls_free(serverctls);
+ return rv;
+ }
+ }
+
+ // Convert our comma separated string to one that the C-SDK will like, i.e.
+ // convert to a char array and add a last NULL element.
+ nsTArray<nsCString> attrArray;
+ ParseString(aAttributes, ',', attrArray);
+ char **attrs = nullptr;
+ uint32_t origLength = attrArray.Length();
+ if (origLength)
+ {
+ attrs = static_cast<char **> (NS_Alloc((origLength + 1) * sizeof(char *)));
+ if (!attrs)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (uint32_t i = 0; i < origLength; ++i)
+ attrs[i] = ToNewCString(attrArray[i]);
+
+ attrs[origLength] = 0;
+ }
+
+ // XXX deal with timeout here
+ int retVal = ldap_search_ext(mConnectionHandle,
+ PromiseFlatCString(aBaseDn).get(),
+ aScope, PromiseFlatCString(aFilter).get(),
+ attrs, 0, serverctls, clientctls, 0,
+ aSizeLimit, &mMsgID);
+
+ // clean up
+ ldap_controls_free(serverctls);
+ ldap_controls_free(clientctls);
+ // The last entry is null, so no need to free that.
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(origLength, attrs);
+
+ rv = TranslateLDAPErrorToNSError(retVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ //
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+ if (NS_FAILED(rv)) {
+ switch (rv) {
+ case NS_ERROR_OUT_OF_MEMORY:
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ default:
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ NS_ERROR("nsLDAPOperation::SearchExt(): unexpected error in "
+ "mConnection->AddPendingOperation");
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::GetMessageID(int32_t *aMsgID)
+{
+ if (!aMsgID) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ *aMsgID = mMsgID;
+
+ return NS_OK;
+}
+
+// as far as I can tell from reading the LDAP C SDK code, abandoning something
+// that has already been abandoned does not return an error
+//
+NS_IMETHODIMP
+nsLDAPOperation::AbandonExt()
+{
+ nsresult rv;
+ nsresult retStatus = NS_OK;
+
+ if (!mMessageListener || mMsgID == 0) {
+ NS_ERROR("nsLDAPOperation::AbandonExt(): mMessageListener or "
+ "mMsgId not initialized");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // XXX handle controls here
+ if (mServerControls || mClientControls) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ rv = TranslateLDAPErrorToNSError(ldap_abandon_ext(mConnectionHandle,
+ mMsgID, 0, 0));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // try to remove it from the pendingOperations queue, if it's there.
+ // even if something goes wrong here, the abandon() has already succeeded
+ // succeeded (and there's nothing else the caller can reasonably do),
+ // so we only pay attention to this in debug builds.
+ //
+ // check mConnection in case we're getting bit by
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=239729, wherein we
+ // theorize that ::Clearing the operation is nulling out the mConnection
+ // from another thread.
+ if (mConnection)
+ {
+ rv = mConnection->RemovePendingOperation(mMsgID);
+
+ if (NS_FAILED(rv)) {
+ // XXXdmose should we keep AbandonExt from happening on multiple
+ // threads at the same time? that's when this condition is most
+ // likely to occur. i _think_ the LDAP C SDK is ok with this; need
+ // to verify.
+ //
+ NS_WARNING("nsLDAPOperation::AbandonExt: "
+ "mConnection->RemovePendingOperation(this) failed.");
+ }
+ }
+
+ return retStatus;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::GetClientControls(nsIMutableArray **aControls)
+{
+ NS_IF_ADDREF(*aControls = mClientControls);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SetClientControls(nsIMutableArray *aControls)
+{
+ mClientControls = aControls;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPOperation::GetServerControls(nsIMutableArray **aControls)
+{
+ NS_IF_ADDREF(*aControls = mServerControls);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPOperation::SetServerControls(nsIMutableArray *aControls)
+{
+ mServerControls = aControls;
+ return NS_OK;
+}
+
+// wrappers for ldap_add_ext
+//
+nsresult
+nsLDAPOperation::AddExt(const char *base,
+ nsIArray *mods,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls)
+{
+ if (!mMessageListener) {
+ NS_ERROR("nsLDAPOperation::AddExt(): mMessageListener not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LDAPMod **attrs = 0;
+ int retVal = LDAP_SUCCESS;
+ uint32_t modCount = 0;
+ nsresult rv = mods->GetLength(&modCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mods && modCount) {
+ attrs = static_cast<LDAPMod **>(moz_xmalloc((modCount + 1) *
+ sizeof(LDAPMod *)));
+ if (!attrs) {
+ NS_ERROR("nsLDAPOperation::AddExt: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAutoCString type;
+ uint32_t index;
+ for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) {
+ attrs[index] = new LDAPMod();
+
+ if (!attrs[index])
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsILDAPModification> modif(do_QueryElementAt(mods, index, &rv));
+ if (NS_FAILED(rv))
+ break;
+
+#ifdef NS_DEBUG
+ int32_t operation;
+ NS_ASSERTION(NS_SUCCEEDED(modif->GetOperation(&operation)) &&
+ ((operation & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD),
+ "AddExt can only add.");
+#endif
+
+ attrs[index]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
+
+ nsresult rv = modif->GetType(type);
+ if (NS_FAILED(rv))
+ break;
+
+ attrs[index]->mod_type = ToNewCString(type);
+
+ rv = CopyValues(modif, &attrs[index]->mod_bvalues);
+ if (NS_FAILED(rv))
+ break;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ attrs[modCount] = 0;
+
+ retVal = ldap_add_ext(mConnectionHandle, base, attrs,
+ serverctrls, clientctrls, &mMsgID);
+ }
+ else
+ // reset the modCount so we correctly free the array.
+ modCount = index;
+ }
+
+ for (uint32_t counter = 0; counter < modCount; ++counter)
+ delete attrs[counter];
+
+ free(attrs);
+
+ return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal);
+}
+
+/**
+ * wrapper for ldap_add_ext(): kicks off an async add request.
+ *
+ * @param aBaseDn Base DN to search
+ * @param aModCount Number of modifications
+ * @param aMods Array of modifications
+ *
+ * XXX doesn't currently handle LDAPControl params
+ *
+ * void addExt (in AUTF8String aBaseDn, in unsigned long aModCount,
+ * [array, size_is (aModCount)] in nsILDAPModification aMods);
+ */
+NS_IMETHODIMP
+nsLDAPOperation::AddExt(const nsACString& aBaseDn,
+ nsIArray *aMods)
+{
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::AddExt(): called with aBaseDn = '%s'",
+ PromiseFlatCString(aBaseDn).get()));
+
+ nsresult rv = AddExt(PromiseFlatCString(aBaseDn).get(), aMods, 0, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+
+ if (NS_FAILED(rv)) {
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::AddExt(): abandoned due to rv %x",
+ rv));
+ }
+ return rv;
+}
+
+// wrappers for ldap_delete_ext
+//
+nsresult
+nsLDAPOperation::DeleteExt(const char *base,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls)
+{
+ if (!mMessageListener) {
+ NS_ERROR("nsLDAPOperation::DeleteExt(): mMessageListener not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return TranslateLDAPErrorToNSError(ldap_delete_ext(mConnectionHandle, base,
+ serverctrls, clientctrls,
+ &mMsgID));
+}
+
+/**
+ * wrapper for ldap_delete_ext(): kicks off an async delete request.
+ *
+ * @param aBaseDn Base DN to delete
+ *
+ * XXX doesn't currently handle LDAPControl params
+ *
+ * void deleteExt(in AUTF8String aBaseDn);
+ */
+NS_IMETHODIMP
+nsLDAPOperation::DeleteExt(const nsACString& aBaseDn)
+{
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::DeleteExt(): called with aBaseDn = '%s'",
+ PromiseFlatCString(aBaseDn).get()));
+
+ nsresult rv = DeleteExt(PromiseFlatCString(aBaseDn).get(), 0, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+
+ if (NS_FAILED(rv)) {
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::AddExt(): abandoned due to rv %x",
+ rv));
+ }
+ return rv;
+}
+
+// wrappers for ldap_modify_ext
+//
+nsresult
+nsLDAPOperation::ModifyExt(const char *base,
+ nsIArray *mods,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls)
+{
+ if (!mMessageListener) {
+ NS_ERROR("nsLDAPOperation::ModifyExt(): mMessageListener not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LDAPMod **attrs = 0;
+ int retVal = 0;
+ uint32_t modCount = 0;
+ nsresult rv = mods->GetLength(&modCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (modCount && mods) {
+ attrs = static_cast<LDAPMod **>(moz_xmalloc((modCount + 1) *
+ sizeof(LDAPMod *)));
+ if (!attrs) {
+ NS_ERROR("nsLDAPOperation::ModifyExt: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAutoCString type;
+ uint32_t index;
+ for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) {
+ attrs[index] = new LDAPMod();
+ if (!attrs[index])
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsILDAPModification> modif(do_QueryElementAt(mods, index, &rv));
+ if (NS_FAILED(rv))
+ break;
+
+ int32_t operation;
+ nsresult rv = modif->GetOperation(&operation);
+ if (NS_FAILED(rv))
+ break;
+
+ attrs[index]->mod_op = operation | LDAP_MOD_BVALUES;
+
+ rv = modif->GetType(type);
+ if (NS_FAILED(rv))
+ break;
+
+ attrs[index]->mod_type = ToNewCString(type);
+
+ rv = CopyValues(modif, &attrs[index]->mod_bvalues);
+ if (NS_FAILED(rv))
+ break;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ attrs[modCount] = 0;
+
+ retVal = ldap_modify_ext(mConnectionHandle, base, attrs,
+ serverctrls, clientctrls, &mMsgID);
+ }
+ else
+ // reset the modCount so we correctly free the array.
+ modCount = index;
+
+ }
+
+ for (uint32_t counter = 0; counter < modCount; ++counter)
+ delete attrs[counter];
+
+ free(attrs);
+
+ return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal);
+}
+
+/**
+ * wrapper for ldap_modify_ext(): kicks off an async modify request.
+ *
+ * @param aBaseDn Base DN to modify
+ * @param aModCount Number of modifications
+ * @param aMods Array of modifications
+ *
+ * XXX doesn't currently handle LDAPControl params
+ *
+ * void modifyExt (in AUTF8String aBaseDn, in unsigned long aModCount,
+ * [array, size_is (aModCount)] in nsILDAPModification aMods);
+ */
+NS_IMETHODIMP
+nsLDAPOperation::ModifyExt(const nsACString& aBaseDn,
+ nsIArray *aMods)
+{
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::ModifyExt(): called with aBaseDn = '%s'",
+ PromiseFlatCString(aBaseDn).get()));
+
+ nsresult rv = ModifyExt(PromiseFlatCString(aBaseDn).get(),
+ aMods, 0, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+
+ if (NS_FAILED(rv)) {
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::AddExt(): abandoned due to rv %x",
+ rv));
+ }
+ return rv;
+}
+
+// wrappers for ldap_rename
+//
+nsresult
+nsLDAPOperation::Rename(const char *base,
+ const char *newRDn,
+ const char *newParent,
+ bool deleteOldRDn,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls)
+{
+ if (!mMessageListener) {
+ NS_ERROR("nsLDAPOperation::Rename(): mMessageListener not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return TranslateLDAPErrorToNSError(ldap_rename(mConnectionHandle, base,
+ newRDn, newParent,
+ deleteOldRDn, serverctrls,
+ clientctrls, &mMsgID));
+}
+
+/**
+ * wrapper for ldap_rename(): kicks off an async rename request.
+ *
+ * @param aBaseDn Base DN to rename
+ * @param aNewRDn New relative DN
+ * @param aNewParent DN of the new parent under which to move the
+ *
+ * XXX doesn't currently handle LDAPControl params
+ *
+ * void rename(in AUTF8String aBaseDn, in AUTF8String aNewRDn,
+ * in AUTF8String aNewParent, in boolean aDeleteOldRDn);
+ */
+NS_IMETHODIMP
+nsLDAPOperation::Rename(const nsACString& aBaseDn,
+ const nsACString& aNewRDn,
+ const nsACString& aNewParent,
+ bool aDeleteOldRDn)
+{
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::Rename(): called with aBaseDn = '%s'",
+ PromiseFlatCString(aBaseDn).get()));
+
+ nsresult rv = Rename(PromiseFlatCString(aBaseDn).get(),
+ PromiseFlatCString(aNewRDn).get(),
+ PromiseFlatCString(aNewParent).get(),
+ aDeleteOldRDn, 0, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the connection knows where to call back once the messages
+ // for this operation start coming in
+ rv = mConnection->AddPendingOperation(mMsgID, this);
+
+ if (NS_FAILED(rv)) {
+ (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPOperation::AddExt(): abandoned due to rv %x",
+ rv));
+ }
+ return rv;
+}
+
+// wrappers for ldap_search_ext
+//
+
+/* static */
+nsresult
+nsLDAPOperation::CopyValues(nsILDAPModification* aMod, berval*** aBValues)
+{
+ nsCOMPtr<nsIArray> values;
+ nsresult rv = aMod->GetValues(getter_AddRefs(values));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t valuesCount;
+ rv = values->GetLength(&valuesCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aBValues = static_cast<berval **>
+ (moz_xmalloc((valuesCount + 1) *
+ sizeof(berval *)));
+ if (!*aBValues)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t valueIndex;
+ for (valueIndex = 0; valueIndex < valuesCount; ++valueIndex) {
+ nsCOMPtr<nsILDAPBERValue> value(do_QueryElementAt(values, valueIndex, &rv));
+
+ berval* bval = new berval;
+ if (NS_FAILED(rv) || !bval) {
+ for (uint32_t counter = 0;
+ counter < valueIndex && counter < valuesCount;
+ ++counter)
+ delete (*aBValues)[valueIndex];
+
+ free(*aBValues);
+ delete bval;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ value->Get((uint32_t*)&bval->bv_len,
+ (uint8_t**)&bval->bv_val);
+ (*aBValues)[valueIndex] = bval;
+ }
+
+ (*aBValues)[valuesCount] = 0;
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPOperation.h b/ldap/xpcom/src/nsLDAPOperation.h
new file mode 100644
index 000000000..7a74046d7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPOperation.h
@@ -0,0 +1,104 @@
+/* -*- 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/. */
+
+#ifndef _nsLDAPOperation_h_
+#define _nsLDAPOperation_h_
+
+#include "ldap.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsILDAPMessageListener.h"
+#include "nsStringGlue.h"
+#include "nsIMutableArray.h"
+#include "nsLDAPConnection.h"
+
+// 97a479d0-9a44-47c6-a17a-87f9b00294bb
+#define NS_LDAPOPERATION_CID \
+{ 0x97a479d0, 0x9a44, 0x47c6, \
+ { 0xa1, 0x7a, 0x87, 0xf9, 0xb0, 0x02, 0x94, 0xbb}}
+
+class nsLDAPOperation : public nsILDAPOperation
+{
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPOPERATION
+
+ // constructor & destructor
+ //
+ nsLDAPOperation();
+
+ /**
+ * used to break cycles
+ */
+ void Clear();
+
+ private:
+ virtual ~nsLDAPOperation();
+
+ /**
+ * wrapper for ldap_add_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult AddExt(const char *base, // base DN to add
+ nsIArray *mods, // Array of modifications
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_delete_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult DeleteExt(const char *base, // base DN to delete
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_modify_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult ModifyExt(const char *base, // base DN to modify
+ nsIArray *mods, // array of modifications
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_rename()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult Rename(const char *base, // base DN to rename
+ const char *newRDn, // new RDN
+ const char *newParent, // DN of the new parent
+ bool deleteOldRDn, // remove old RDN in the entry?
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * Helper function to copy the values of an nsILDAPModification into an
+ * array of berval's.
+ */
+ static nsresult CopyValues(nsILDAPModification* aMod, berval*** aBValues);
+
+ nsCOMPtr<nsILDAPMessageListener> mMessageListener; // results go here
+ nsCOMPtr<nsISupports> mClosure; // private parameter (anything caller desires)
+ RefPtr<nsLDAPConnection> mConnection; // connection this op is on
+
+ LDAP *mConnectionHandle; // cache connection handle
+ nsCString mSavePassword;
+ nsCString mMechanism;
+ nsCOMPtr<nsIAuthModule> mAuthModule;
+ int32_t mMsgID; // opaque handle to outbound message for this op
+
+ nsCOMPtr<nsIMutableArray> mClientControls;
+ nsCOMPtr<nsIMutableArray> mServerControls;
+};
+
+#endif // _nsLDAPOperation_h
diff --git a/ldap/xpcom/src/nsLDAPProtocolHandler.js b/ldap/xpcom/src/nsLDAPProtocolHandler.js
new file mode 100644
index 000000000..71fe082ff
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPProtocolHandler.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const kNetworkProtocolCIDPrefix = "@mozilla.org/network/protocol;1?name=";
+const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
+
+function makeProtocolHandler(aCID, aProtocol, aDefaultPort) {
+ return {
+ classID: Components.ID(aCID),
+ QueryInterface: XPCOMUtils.generateQI([nsIProtocolHandler]),
+
+ scheme: aProtocol,
+ defaultPort: aDefaultPort,
+ protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
+ nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
+ nsIProtocolHandler.ALLOWS_PROXY,
+
+ newURI: function (aSpec, aOriginCharset, aBaseURI) {
+ var url = Components.classes["@mozilla.org/network/ldap-url;1"]
+ .createInstance(Components.interfaces.nsIURI);
+
+ if (url instanceof Components.interfaces.nsILDAPURL)
+ url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD,
+ aDefaultPort, aSpec, aOriginCharset, aBaseURI);
+
+ return url;
+ },
+
+ newChannel: function (aURI) {
+ return this.newChannel2(aURI, null);
+ },
+
+ newChannel2: function (aURI, aLoadInfo) {
+ if ("@mozilla.org/network/ldap-channel;1" in Components.classes) {
+ var channel = Components.classes["@mozilla.org/network/ldap-channel;1"]
+ .createInstance(Components.interfaces.nsIChannel);
+ channel.init(aURI);
+ return channel;
+ }
+
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ allowPort: function (port, scheme) {
+ return port == aDefaultPort;
+ }
+ };
+}
+
+function nsLDAPProtocolHandler() {}
+
+nsLDAPProtocolHandler.prototype = makeProtocolHandler("{b3de9249-b0e5-4c12-8d91-c9a434fd80f5}", "ldap", 389);
+
+function nsLDAPSProtocolHandler() {}
+
+nsLDAPSProtocolHandler.prototype = makeProtocolHandler("{c85a5ef2-9c56-445f-b029-76889f2dd29b}", "ldaps", 636);
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsLDAPProtocolHandler,
+ nsLDAPSProtocolHandler]);
diff --git a/ldap/xpcom/src/nsLDAPProtocolModule.cpp b/ldap/xpcom/src/nsLDAPProtocolModule.cpp
new file mode 100644
index 000000000..6f72e7c43
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPProtocolModule.cpp
@@ -0,0 +1,147 @@
+/* -*- 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 "nsIClassInfoImpl.h"
+#include "mozilla/ModuleUtils.h"
+
+#include "nsLDAPInternal.h"
+#include "nsLDAPURL.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPOperation.h"
+#include "nsLDAPMessage.h"
+#include "nsLDAPModification.h"
+#include "nsLDAPServer.h"
+#include "nsLDAPService.h"
+#include "nsLDAPBERValue.h"
+#include "nsLDAPBERElement.h"
+#include "nsLDAPControl.h"
+#ifdef MOZ_PREF_EXTENSIONS
+#include "nsLDAPSyncQuery.h"
+#endif
+#include "ldappr.h"
+#include "mozilla/Logging.h"
+
+// use the default constructor
+//
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPConnection)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPOperation)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPMessage)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPModification, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPServer)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPURL)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERValue)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERElement)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPControl)
+#ifdef MOZ_PREF_EXTENSIONS
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPSyncQuery)
+#endif
+
+NS_DEFINE_NAMED_CID(NS_LDAPCONNECTION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPOPERATION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPMESSAGE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPMODIFICATION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPSERVER_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPURL_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPBERVALUE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPBERELEMENT_CID);
+#ifdef MOZ_PREF_EXTENSIONS
+NS_DEFINE_NAMED_CID(NS_LDAPSYNCQUERY_CID);
+#endif
+NS_DEFINE_NAMED_CID(NS_LDAPCONTROL_CID);
+
+// a table of the CIDs implemented by this module
+//
+
+const mozilla::Module::CIDEntry kLDAPProtocolCIDs[] = {
+ { &kNS_LDAPCONNECTION_CID, false, NULL, nsLDAPConnectionConstructor},
+ { &kNS_LDAPOPERATION_CID, false, NULL, nsLDAPOperationConstructor},
+ { &kNS_LDAPMESSAGE_CID, false, NULL, nsLDAPMessageConstructor},
+ { &kNS_LDAPMODIFICATION_CID, false, NULL, nsLDAPModificationConstructor},
+ { &kNS_LDAPSERVER_CID, false, NULL, nsLDAPServerConstructor},
+ { &kNS_LDAPSERVICE_CID, false, NULL, nsLDAPServiceConstructor},
+ { &kNS_LDAPURL_CID, false, NULL, nsLDAPURLConstructor},
+ { &kNS_LDAPBERVALUE_CID, false, NULL, nsLDAPBERValueConstructor},
+ { &kNS_LDAPBERELEMENT_CID, false, NULL, nsLDAPBERElementConstructor},
+#ifdef MOZ_PREF_EXTENSIONS
+ { &kNS_LDAPSYNCQUERY_CID, false, NULL, nsLDAPSyncQueryConstructor},
+#endif
+ { &kNS_LDAPCONTROL_CID, false, NULL, nsLDAPControlConstructor},
+ { NULL }
+};
+
+
+const mozilla::Module::ContractIDEntry kLDAPProtocolContracts[] = {
+ { "@mozilla.org/network/ldap-connection;1", &kNS_LDAPCONNECTION_CID},
+ { "@mozilla.org/network/ldap-operation;1", &kNS_LDAPOPERATION_CID},
+ { "@mozilla.org/network/ldap-message;1", &kNS_LDAPMESSAGE_CID},
+ { "@mozilla.org/network/ldap-modification;1", &kNS_LDAPMODIFICATION_CID},
+ { "@mozilla.org/network/ldap-server;1", &kNS_LDAPSERVER_CID},
+ { "@mozilla.org/network/ldap-service;1", &kNS_LDAPSERVICE_CID},
+ { "@mozilla.org/network/ldap-url;1", &kNS_LDAPURL_CID},
+ { "@mozilla.org/network/ldap-ber-value;1", &kNS_LDAPBERVALUE_CID},
+ { "@mozilla.org/network/ldap-ber-element;1", &kNS_LDAPBERELEMENT_CID},
+#ifdef MOZ_PREF_EXTENSIONS
+ { "@mozilla.org/ldapsyncquery;1", &kNS_LDAPSYNCQUERY_CID},
+#endif
+ { "@mozilla.org/network/ldap-control;1", &kNS_LDAPCONTROL_CID},
+ { NULL }
+};
+
+static nsresult
+nsLDAPInitialize()
+{
+#ifdef PR_LOGGING
+ gLDAPLogModule = PR_NewLogModule("ldap");
+ if (!gLDAPLogModule) {
+ PR_fprintf(PR_STDERR,
+ "nsLDAP_Initialize(): PR_NewLogModule() failed\n");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+#endif
+
+ // use NSPR under the hood for all networking
+ //
+ int rv = prldap_install_routines( NULL, 1 /* shared */ );
+
+ if (rv != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPInitialize(): pr_ldap_install_routines() failed: %s\n",
+ ldap_err2string(rv)));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Never block for more than 10000 milliseconds (ie 10 seconds) doing any
+ // sort of I/O operation.
+ //
+ rv = prldap_set_session_option(0, 0, PRLDAP_OPT_IO_MAX_TIMEOUT,
+ 10000);
+ if (rv != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPInitialize(): error setting PRLDAP_OPT_IO_MAX_TIMEOUT:"
+ " %s\n", ldap_err2string(rv)));
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static const mozilla::Module kLDAPProtocolModule = {
+ mozilla::Module::kVersion,
+ kLDAPProtocolCIDs,
+ kLDAPProtocolContracts,
+ NULL,
+ NULL,
+ nsLDAPInitialize,
+ NULL
+};
+
+NSMODULE_DEFN(nsLDAPProtocolModule) = &kLDAPProtocolModule;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gLDAPLogModule = 0;
+#endif
diff --git a/ldap/xpcom/src/nsLDAPSecurityGlue.cpp b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
new file mode 100644
index 000000000..c39723a72
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
@@ -0,0 +1,342 @@
+/* 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/. */
+
+// Only build this code if PSM is being built also
+//
+#include "nsLDAPInternal.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsNetCID.h"
+#include "nsISocketProvider.h"
+#include "nsISSLSocketControl.h"
+#include "nsString.h"
+#include "nsMemory.h"
+#include "plstr.h"
+#include "ldap.h"
+#include "ldappr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// LDAP per-session data structure.
+//
+typedef struct {
+ char *hostname;
+ LDAP_X_EXTIOF_CLOSE_CALLBACK *realClose;
+ LDAP_X_EXTIOF_CONNECT_CALLBACK *realConnect;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *realDisposeHandle;
+} nsLDAPSSLSessionClosure;
+
+// LDAP per-socket data structure.
+//
+typedef struct {
+ nsLDAPSSLSessionClosure *sessionClosure; /* session info */
+} nsLDAPSSLSocketClosure;
+
+// free the per-socket data structure as necessary
+//
+static void
+nsLDAPSSLFreeSocketClosure(nsLDAPSSLSocketClosure **aClosure)
+{
+ if (aClosure && *aClosure) {
+ free(*aClosure);
+ *aClosure = nullptr;
+ }
+}
+
+// Replacement close() function, which cleans up local stuff associated
+// with this socket, and then calls the real close function.
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLClose(int s, struct lextiof_socket_private *socketarg)
+{
+ PRLDAPSocketInfo socketInfo;
+ nsLDAPSSLSocketClosure *socketClosure;
+ nsLDAPSSLSessionClosure *sessionClosure;
+
+ // get the socketInfo associated with this socket
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(s, socketarg, &socketInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLClose(): prldap_get_socket_info() failed\n");
+ return -1;
+ }
+
+ // save off the session closure data in an automatic, since we're going to
+ // need to call through it
+ //
+ socketClosure = reinterpret_cast<nsLDAPSSLSocketClosure *>
+ (socketInfo.soinfo_appdata);
+ sessionClosure = socketClosure->sessionClosure;
+
+ // free the socket closure data
+ //
+ nsLDAPSSLFreeSocketClosure(
+ reinterpret_cast<nsLDAPSSLSocketClosure **>
+ (&socketInfo.soinfo_appdata));
+
+ // call the real close function
+ //
+ return (*(sessionClosure->realClose))(s, socketarg);
+}
+
+// Replacement connection function. Calls the real connect function,
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLConnect(const char *hostlist, int defport, int timeout,
+ unsigned long options,
+ struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp )
+{
+ PRLDAPSocketInfo socketInfo;
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSocketClosure *socketClosure = nullptr;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ int intfd = -1;
+ nsCOMPtr <nsISupports> securityInfo;
+ nsCOMPtr <nsISocketProvider> tlsSocketProvider;
+ nsCOMPtr <nsISSLSocketControl> sslSocketControl;
+ nsresult rv;
+
+ // Ensure secure option is set. Also, clear secure bit in options
+ // the we pass to the standard connect() function (since it doesn't know
+ // how to handle the secure option).
+ //
+ NS_ASSERTION(options & LDAP_X_EXTIOF_OPT_SECURE,
+ "nsLDAPSSLConnect(): called for non-secure connection");
+ options &= ~LDAP_X_EXTIOF_OPT_SECURE;
+
+ // Retrieve session info. so we can store a pointer to our session info.
+ // in our socket info. later.
+ //
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(nullptr, sessionarg, &sessionInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get session info");
+ return -1;
+ }
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+
+ // Call the real connect() callback to make the TCP connection. If it
+ // succeeds, *socketargp is set.
+ //
+ intfd = (*(sessionClosure->realConnect))(hostlist, defport, timeout,
+ options, sessionarg, socketargp);
+ if ( intfd < 0 ) {
+ PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
+ ("nsLDAPSSLConnect(): standard connect() function returned %d",
+ intfd));
+ return intfd;
+ }
+
+ // Retrieve socket info from the newly created socket so that we
+ // have the PRFileDesc onto which we will be layering SSL.
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket info");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // Allocate a structure to hold our socket-specific data.
+ //
+ socketClosure = static_cast<nsLDAPSSLSocketClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSocketClosure)));
+ if (!socketClosure) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to allocate socket closure");
+ goto close_socket_and_exit_with_error;
+ }
+ memset(socketClosure, 0, sizeof(nsLDAPSSLSocketClosure));
+ socketClosure->sessionClosure = sessionClosure;
+
+ // Add the NSPR layer for SSL provided by PSM to this socket.
+ //
+ tlsSocketProvider = do_GetService(NS_STARTTLSSOCKETPROVIDER_CONTRACTID,
+ &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket provider service");
+ goto close_socket_and_exit_with_error;
+ }
+ // XXXdmose: Note that hostlist can be a list of hosts (in the
+ // current XPCOM SDK code, it will always be a list of IP
+ // addresses). Because of this, we need to use
+ // sessionClosure->hostname which was passed in separately to tell
+ // AddToSocket what to match the name in the certificate against.
+ // What exactly happen will happen when this is used with some IP
+ // address in the list other than the first one is not entirely
+ // clear, and I suspect it may depend on the format of the name in
+ // the certificate. Need to investigate.
+ //
+ rv = tlsSocketProvider->AddToSocket(PR_AF_INET,
+ sessionClosure->hostname,
+ defport,
+ nullptr,
+ mozilla::NeckoOriginAttributes(),
+ 0,
+ socketInfo.soinfo_prfd,
+ getter_AddRefs(securityInfo));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // If possible we want to avoid using SSLv2, as this can confuse
+ // some directory servers (notably the netscape 4.1 ds). The only
+ // way that PSM provides for us to do this is to use a socket that can
+ // be used for the STARTTLS protocol, because the STARTTLS protocol disallows
+ // the use of SSLv2.
+ // (Thanks to Brian Ryner for helping figure this out).
+ //
+ sslSocketControl = do_QueryInterface(securityInfo, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to QI to nsISSLSocketControl");
+ } else {
+ rv = sslSocketControl->StartTLS();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): StartTLS failed");
+ }
+ }
+
+ // Attach our closure to the socketInfo.
+ //
+ socketInfo.soinfo_appdata = reinterpret_cast<prldap_socket_private *>
+ (socketClosure);
+ if (prldap_set_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS ) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to set socket info");
+ }
+ return intfd; // success
+
+close_socket_and_exit_with_error:
+ if (socketInfo.soinfo_prfd) {
+ PR_Close(socketInfo.soinfo_prfd);
+ }
+ if (socketClosure) {
+ nsLDAPSSLFreeSocketClosure(&socketClosure);
+ }
+ if ( intfd >= 0 && *socketargp ) {
+ (*(sessionClosure->realClose))(intfd, *socketargp);
+ }
+ return -1;
+
+}
+
+// Free data associated with this session (LDAP *) as necessary.
+//
+static void
+nsLDAPSSLFreeSessionClosure(nsLDAPSSLSessionClosure **aSessionClosure)
+{
+ if (aSessionClosure && *aSessionClosure) {
+
+ // free the hostname
+ //
+ if ( (*aSessionClosure)->hostname ) {
+ PL_strfree((*aSessionClosure)->hostname);
+ (*aSessionClosure)->hostname = nullptr;
+ }
+
+ // free the structure itself
+ //
+ free(*aSessionClosure);
+ *aSessionClosure = nullptr;
+ }
+}
+
+// Replacement session handle disposal code. First cleans up our local
+// stuff, then calls the original session handle disposal function.
+//
+extern "C" void LDAP_CALLBACK
+nsLDAPSSLDisposeHandle(LDAP *ld, struct lextiof_session_private *sessionarg)
+{
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
+
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(ld, nullptr, &sessionInfo) == LDAP_SUCCESS) {
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+ disposehdl_fn = sessionClosure->realDisposeHandle;
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ (*disposehdl_fn)(ld, sessionarg);
+ }
+}
+
+// Installs appropriate routines and data for making this connection
+// handle SSL. The aHostName is ultimately passed to PSM and is used to
+// validate certificates.
+//
+nsresult
+nsLDAPInstallSSL( LDAP *ld, const char *aHostName)
+{
+ struct ldap_x_ext_io_fns iofns;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ PRLDAPSessionInfo sessionInfo;
+
+ // Allocate our own session information.
+ //
+ sessionClosure = static_cast<nsLDAPSSLSessionClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSessionClosure)));
+ if (!sessionClosure) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memset(sessionClosure, 0, sizeof(nsLDAPSSLSessionClosure));
+
+ // Override a few functions, saving a pointer to the original function
+ // in each case so we can call it from our SSL savvy functions.
+ //
+ memset(&iofns, 0, sizeof(iofns));
+ iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ if (ldap_get_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): unexpected error getting"
+ " LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Make a copy of the hostname to pass to AddToSocket later
+ //
+ sessionClosure->hostname = PL_strdup(aHostName);
+ if (!sessionClosure->hostname) {
+ NS_ERROR("nsLDAPInstallSSL(): PL_strdup failed\n");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Override functions
+ //
+ sessionClosure->realClose = iofns.lextiof_close;
+ iofns.lextiof_close = nsLDAPSSLClose;
+ sessionClosure->realConnect = iofns.lextiof_connect;
+ iofns.lextiof_connect = nsLDAPSSLConnect;
+ sessionClosure->realDisposeHandle = iofns.lextiof_disposehandle;
+ iofns.lextiof_disposehandle = nsLDAPSSLDisposeHandle;
+
+ if (ldap_set_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Store session info. for later retrieval.
+ //
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ sessionInfo.seinfo_appdata = reinterpret_cast<prldap_session_private *>
+ (sessionClosure);
+ if (prldap_set_session_info(ld, nullptr, &sessionInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info");
+ free(sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPServer.cpp b/ldap/xpcom/src/nsLDAPServer.cpp
new file mode 100644
index 000000000..34504c5c7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPServer.cpp
@@ -0,0 +1,133 @@
+/* -*- 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 "nsLDAPServer.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPServer, nsILDAPServer)
+
+nsLDAPServer::nsLDAPServer()
+ : mSizeLimit(0),
+ mProtocolVersion(nsILDAPConnection::VERSION3)
+{
+}
+
+nsLDAPServer::~nsLDAPServer()
+{
+}
+
+// attribute wstring key;
+NS_IMETHODIMP nsLDAPServer::GetKey(char16_t **_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetKey: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = ToNewUnicode(mKey);
+ if (!*_retval) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetKey(const char16_t *aKey)
+{
+ mKey = aKey;
+ return NS_OK;
+}
+
+// attribute AUTF8String username;
+NS_IMETHODIMP nsLDAPServer::GetUsername(nsACString& _retval)
+{
+ _retval.Assign(mUsername);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetUsername(const nsACString& aUsername)
+{
+ mUsername.Assign(aUsername);
+ return NS_OK;
+}
+
+// attribute AUTF8String password;
+NS_IMETHODIMP nsLDAPServer::GetPassword(nsACString& _retval)
+{
+ _retval.Assign(mPassword);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetPassword(const nsACString& aPassword)
+{
+ mPassword.Assign(aPassword);
+ return NS_OK;
+}
+
+// attribute AUTF8String binddn;
+NS_IMETHODIMP nsLDAPServer::GetBinddn(nsACString& _retval)
+{
+ _retval.Assign(mBindDN);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetBinddn(const nsACString& aBindDN)
+{
+ mBindDN.Assign(aBindDN);
+ return NS_OK;
+}
+
+// attribute unsigned long sizelimit;
+NS_IMETHODIMP nsLDAPServer::GetSizelimit(uint32_t *_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetSizelimit: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = mSizeLimit;
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetSizelimit(uint32_t aSizeLimit)
+{
+ mSizeLimit = aSizeLimit;
+ return NS_OK;
+}
+
+// attribute nsILDAPURL url;
+NS_IMETHODIMP nsLDAPServer::GetUrl(nsILDAPURL **_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetUrl: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_IF_ADDREF(*_retval = mURL);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetUrl(nsILDAPURL *aURL)
+{
+ mURL = aURL;
+ return NS_OK;
+}
+
+// attribute long protocolVersion
+NS_IMETHODIMP nsLDAPServer::GetProtocolVersion(uint32_t *_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetProtocolVersion: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = mProtocolVersion;
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetProtocolVersion(uint32_t aVersion)
+{
+ if (aVersion != nsILDAPConnection::VERSION2 &&
+ aVersion != nsILDAPConnection::VERSION3) {
+ NS_ERROR("nsLDAPServer::SetProtocolVersion: invalid version");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mProtocolVersion = aVersion;
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPServer.h b/ldap/xpcom/src/nsLDAPServer.h
new file mode 100644
index 000000000..e8ec5da35
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPServer.h
@@ -0,0 +1,39 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsILDAPServer.h"
+#include "nsILDAPURL.h"
+
+// 8bbbaa54-f316-4271-87c3-d52b5b1c1f5b
+#define NS_LDAPSERVER_CID \
+{ 0x8bbbaa54, 0xf316, 0x4271, \
+ { 0x87, 0xc3, 0xd5, 0x2b, 0x5b, 0x1c, 0x1f, 0x5b}}
+
+class nsLDAPServer : public nsILDAPServer
+{
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPSERVER
+
+ // Constructor & destructor
+ //
+ nsLDAPServer();
+
+ protected:
+ virtual ~nsLDAPServer();
+
+ nsString mKey; // Unique identifier for this server object
+ nsCString mUsername; // Username / UID
+ nsCString mPassword; // Password to bind with
+ nsCString mBindDN; // DN associated with the UID above
+ uint32_t mSizeLimit; // Limit the LDAP search to this # of entries
+ uint32_t mProtocolVersion; // What version of LDAP to use?
+ // This "links" to a LDAP URL object, which holds further information
+ // related to the LDAP server. Like Host, port, base-DN and scope.
+ nsCOMPtr<nsILDAPURL> mURL;
+};
diff --git a/ldap/xpcom/src/nsLDAPService.cpp b/ldap/xpcom/src/nsLDAPService.cpp
new file mode 100644
index 000000000..9445a5f33
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPService.cpp
@@ -0,0 +1,985 @@
+/* -*- 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 "nsLDAPService.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPOperation.h"
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+#include "nsILDAPURL.h"
+#include "nsMemory.h"
+#include "nsILDAPErrors.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/Logging.h"
+
+using namespace mozilla;
+
+// Constants for CIDs used here.
+//
+static NS_DEFINE_CID(kLDAPConnectionCID, NS_LDAPCONNECTION_CID);
+static NS_DEFINE_CID(kLDAPOperationCID, NS_LDAPOPERATION_CID);
+
+// First we provide all the methods for the "local" class
+// nsLDAPServiceEntry.
+//
+
+// constructor
+//
+nsLDAPServiceEntry::nsLDAPServiceEntry()
+ : mLeases(0),
+ mTimestamp(0),
+ mDelete(false),
+ mRebinding(false)
+
+{
+}
+
+// Init function
+//
+bool nsLDAPServiceEntry::Init()
+{
+ return true;
+}
+
+// Set/Get the timestamp when this server was last used. We might have
+// to use an "interval" here instead, see Bug #76887.
+//
+PRTime nsLDAPServiceEntry::GetTimestamp()
+{
+ return mTimestamp;
+}
+void nsLDAPServiceEntry::SetTimestamp()
+{
+ mTimestamp = PR_Now();
+}
+
+
+// Increment, decrement and Get the leases. This code might go away
+// with bug #75954.
+//
+void nsLDAPServiceEntry::IncrementLeases()
+{
+ mLeases++;
+}
+bool nsLDAPServiceEntry::DecrementLeases()
+{
+ if (!mLeases) {
+ return false;
+ }
+ mLeases--;
+
+ return true;
+}
+uint32_t nsLDAPServiceEntry::GetLeases()
+{
+ return mLeases;
+}
+
+// Get/Set the nsLDAPServer object for this entry.
+//
+already_AddRefed<nsILDAPServer> nsLDAPServiceEntry::GetServer()
+{
+ nsCOMPtr<nsILDAPServer> server = mServer;
+ return server.forget();
+}
+bool nsLDAPServiceEntry::SetServer(nsILDAPServer *aServer)
+{
+ if (!aServer) {
+ return false;
+ }
+ mServer = aServer;
+
+ return true;
+}
+
+// Get/Set/Clear the nsLDAPConnection object for this entry.
+//
+already_AddRefed<nsILDAPConnection> nsLDAPServiceEntry::GetConnection()
+{
+ nsCOMPtr<nsILDAPConnection> conn = mConnection;
+ return conn.forget();
+}
+void nsLDAPServiceEntry::SetConnection(nsILDAPConnection *aConnection)
+{
+ mConnection = aConnection;
+}
+
+// Get/Set the nsLDAPMessage object for this entry (it's a "cache").
+//
+already_AddRefed<nsILDAPMessage> nsLDAPServiceEntry::GetMessage()
+{
+ nsCOMPtr<nsILDAPMessage> message = mMessage;
+ return message.forget();
+}
+void nsLDAPServiceEntry::SetMessage(nsILDAPMessage *aMessage)
+{
+ mMessage = aMessage;
+}
+
+// Push/Pop pending listeners/callback for this server entry. This is
+// implemented as a "stack" on top of the nsCOMArray, since we can
+// potentially have more than one listener waiting for the connection
+// to be available for consumption.
+//
+already_AddRefed<nsILDAPMessageListener> nsLDAPServiceEntry::PopListener()
+{
+ if (mListeners.IsEmpty()) {
+ return 0;
+ }
+
+ nsCOMPtr<nsILDAPMessageListener> listener = mListeners[0];
+ mListeners.RemoveObjectAt(0);
+ return listener.forget();
+}
+bool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener)
+{
+ return mListeners.AppendObject(listener);
+}
+
+// Mark this server to currently be rebinding. This is to avoid a
+// race condition where multiple consumers could potentially request
+// to reconnect the connection.
+//
+bool nsLDAPServiceEntry::IsRebinding()
+{
+ return mRebinding;
+}
+void nsLDAPServiceEntry::SetRebinding(bool aState)
+{
+ mRebinding = aState;
+}
+
+// Mark a service entry for deletion, this is "dead" code right now,
+// see bug #75966.
+//
+bool nsLDAPServiceEntry::DeleteEntry()
+{
+ mDelete = true;
+
+ return true;
+}
+// This is the end of the nsLDAPServiceEntry class
+
+
+// Here begins the implementation for nsLDAPService
+//
+NS_IMPL_ISUPPORTS(nsLDAPService,
+ nsILDAPService,
+ nsILDAPMessageListener)
+
+
+// constructor
+//
+nsLDAPService::nsLDAPService()
+ : mLock("nsLDAPService.mLock")
+{
+}
+
+// destructor
+//
+nsLDAPService::~nsLDAPService()
+{
+}
+
+// Initializer
+//
+nsresult nsLDAPService::Init()
+{
+ return NS_OK;
+}
+
+// void addServer (in nsILDAPServer aServer);
+NS_IMETHODIMP nsLDAPService::AddServer(nsILDAPServer *aServer)
+{
+ nsLDAPServiceEntry *entry;
+ nsString key;
+ nsresult rv;
+
+ if (!aServer) {
+ NS_ERROR("nsLDAPService::AddServer: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Set up the hash key for the server entry
+ //
+ rv = aServer->GetKey(getter_Copies(key));
+ if (NS_FAILED(rv)) {
+ // Only pass along errors we are aware of
+ if ((rv == NS_ERROR_OUT_OF_MEMORY) || (rv == NS_ERROR_NULL_POINTER))
+ return rv;
+ else
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create the new service server entry, and add it into the hash table
+ //
+ entry = new nsLDAPServiceEntry;
+ if (!entry) {
+ NS_ERROR("nsLDAPService::AddServer: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!entry->Init()) {
+ delete entry;
+ NS_ERROR("nsLDAPService::AddServer: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!entry->SetServer(aServer)) {
+ delete entry;
+ return NS_ERROR_FAILURE;
+ }
+
+ // We increment the refcount here for the server entry, when
+ // we purge a server completely from the service (TBD), we
+ // need to decrement the counter as well.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (mServers.Get(key)) {
+ // Collision detected, lets just throw away this service entry
+ // and keep the old one.
+ //
+ delete entry;
+ return NS_ERROR_FAILURE;
+ }
+ mServers.Put(key, entry);
+ }
+ NS_ADDREF(aServer);
+
+ return NS_OK;
+}
+
+// void deleteServer (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::DeleteServer(const char16_t *aKey)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ // We should probably rename the key for this entry now that it's
+ // "deleted", so that we can add in a new one with the same ID.
+ // This is bug #77669.
+ //
+ mServers.Get(nsDependentString(aKey), &entry);
+ if (entry) {
+ if (entry->GetLeases() > 0) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->DeleteEntry();
+ } else {
+ // There is no Server entry for this key
+ //
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// nsILDAPServer getServer (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::GetServer(const char16_t *aKey,
+ nsILDAPServer **_retval)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!_retval) {
+ NS_ERROR("nsLDAPService::GetServer: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ *_retval = 0;
+ return NS_ERROR_FAILURE;
+ }
+ if (!(*_retval = entry->GetServer().take())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+//void requestConnection (in wstring aKey,
+// in nsILDAPMessageListener aMessageListener);
+NS_IMETHODIMP nsLDAPService::RequestConnection(const char16_t *aKey,
+ nsILDAPMessageListener *aListener)
+{
+ nsLDAPServiceEntry *entry;
+ nsCOMPtr<nsILDAPConnection> conn;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsresult rv;
+
+ if (!aListener) {
+ NS_ERROR("nsLDAPService::RequestConection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Try to find a possibly cached connection and LDAP message.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+
+ conn = entry->GetConnection();
+ message = entry->GetMessage();
+ }
+
+ if (conn) {
+ if (message) {
+ // We already have the connection, and the message, ready to
+ // be used. This might be confusing, since we actually call
+ // the listener before this function returns, see bug #75899.
+ //
+ aListener->OnLDAPMessage(message);
+ return NS_OK;
+ }
+ } else {
+ rv = EstablishConnection(entry, aListener);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ }
+
+ // We got a new connection, now push the listeners on our stack,
+ // until we get the LDAP message back.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry) ||
+ !entry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+// nsILDAPConnection getConnection (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::GetConnection(const char16_t *aKey,
+ nsILDAPConnection **_retval)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!_retval) {
+ NS_ERROR("nsLDAPService::GetConnection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ *_retval = 0;
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+ entry->IncrementLeases();
+ if (!(*_retval = entry->GetConnection().take())){
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// void releaseConnection (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::ReleaseConnection(const char16_t *aKey)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (entry->GetLeases() > 0) {
+ entry->SetTimestamp();
+ entry->DecrementLeases();
+ } else {
+ // Releasing a non-leased connection is currently a No-Op.
+ //
+ }
+
+ return NS_OK;
+}
+
+// void reconnectConnection (in wstring aKey,
+// in nsILDAPMessageListener aMessageListener);
+NS_IMETHODIMP nsLDAPService::ReconnectConnection(const char16_t *aKey,
+ nsILDAPMessageListener *aListener)
+{
+ nsLDAPServiceEntry *entry;
+ nsresult rv;
+
+ if (!aListener) {
+ NS_ERROR("nsLDAPService::ReconnectConnection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+
+ if (entry->IsRebinding()) {
+ if (!entry->PushListener(aListener)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ // Clear the old connection and message, which should get garbaged
+ // collected now. We mark this as being "rebinding" now, and it
+ // we be marked as finished either if there's an error condition,
+ // or if the OnLDAPMessage() method gets called (i.e. bind() done).
+ //
+ entry->SetMessage(0);
+ entry->SetConnection(0);
+
+ // Get a new connection
+ //
+ entry->SetRebinding(true);
+ }
+
+ rv = EstablishConnection(entry, aListener);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!entry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ entry->SetRebinding(false);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Messages received are passed back via this function.
+ *
+ * @arg aMessage The message that was returned, 0 if none was.
+ *
+ * void OnLDAPMessage (in nsILDAPMessage aMessage)
+ */
+NS_IMETHODIMP
+nsLDAPService::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ nsCOMPtr<nsILDAPOperation> operation;
+ nsCOMPtr<nsILDAPConnection> connection;
+ int32_t messageType;
+
+ // XXXleif: NULL messages are supposedly allowed, but the semantics
+ // are not definted (yet?). This is something to look out for...
+ //
+
+
+ // figure out what sort of message was returned
+ //
+ nsresult rv = aMessage->GetType(&messageType);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPMessage::GetType()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ switch (messageType) {
+ case LDAP_RES_BIND:
+ // a bind has completed
+ //
+ rv = aMessage->GetOperation(getter_AddRefs(operation));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPMessage::GetOperation()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = operation->GetConnection(getter_AddRefs(connection));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPOperation::GetConnection()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Now we have the connection, lets find the corresponding
+ // server entry in the Service.
+ //
+ {
+ nsCOMPtr<nsILDAPMessageListener> listener;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!mConnections.Get(connection, &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ message = entry->GetMessage();
+ if (message) {
+ // We already have a message, lets keep that one.
+ //
+ return NS_ERROR_FAILURE;
+ }
+
+ entry->SetRebinding(false);
+ entry->SetMessage(aMessage);
+
+ // Now process all the pending callbacks/listeners. We
+ // have to make sure to unlock before calling a listener,
+ // since it's likely to call back into us again.
+ //
+ while ((listener = entry->PopListener())) {
+ MutexAutoUnlock unlock(mLock);
+ listener->OnLDAPMessage(aMessage);
+ }
+ }
+ break;
+
+ default:
+ NS_WARNING("nsLDAPService::OnLDAPMessage(): unexpected LDAP message "
+ "received");
+
+ // get the console service so we can log a message
+ //
+ nsCOMPtr<nsIConsoleService> consoleSvc =
+ do_GetService("@mozilla.org/consoleservice;1", &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPChannel::OnLDAPMessage() couldn't get console "
+ "service");
+ break;
+ }
+
+ // log the message
+ //
+ rv = consoleSvc->LogStringMessage(
+ NS_LITERAL_STRING("LDAP: WARNING: nsLDAPService::OnLDAPMessage(): Unexpected LDAP message received").get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPService::OnLDAPMessage(): "
+ "consoleSvc->LogStringMessage() failed");
+ break;
+ }
+
+ return NS_OK;
+}
+
+// void onLDAPInit (in nsILDAPConnection aConn, in nsresult aStatus); */
+//
+NS_IMETHODIMP
+nsLDAPService::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// Helper function to establish an LDAP connection properly.
+//
+nsresult
+nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry,
+ nsILDAPMessageListener *aListener)
+{
+ nsCOMPtr<nsILDAPOperation> operation;
+ nsCOMPtr<nsILDAPServer> server;
+ nsCOMPtr<nsILDAPURL> url;
+ nsCOMPtr<nsILDAPConnection> conn, conn2;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsAutoCString binddn;
+ nsAutoCString password;
+ uint32_t protocolVersion;
+ nsresult rv;
+
+ server = aEntry->GetServer();
+ if (!server) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get username, password, and protocol version from the server entry.
+ //
+ rv = server->GetBinddn(binddn);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = server->GetPassword(password);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = server->GetProtocolVersion(&protocolVersion);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get the host and port out of the URL, which is in the server entry.
+ //
+ rv = server->GetUrl(getter_AddRefs(url));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ // Create a new connection for this server.
+ //
+ conn = do_CreateInstance(kLDAPConnectionCID, &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::EstablishConnection(): could not create "
+ "@mozilla.org/network/ldap-connection;1");
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = conn->Init(url, binddn, this, nullptr, protocolVersion);
+ if (NS_FAILED(rv)) {
+ switch (rv) {
+ // Only pass along errors we are aware of
+ //
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_NOT_AVAILABLE:
+ case NS_ERROR_FAILURE:
+ return rv;
+
+ case NS_ERROR_ILLEGAL_VALUE:
+ default:
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ // Try to detect a collision, i.e. someone established a connection
+ // just before we did. If so, we drop ours. This code is somewhat
+ // similar to what happens in RequestConnection(), i.e. we try to
+ // call the listener directly if possible, and if not, push it on
+ // the stack of pending requests.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ conn2 = aEntry->GetConnection();
+ message = aEntry->GetMessage();
+ }
+
+ if (conn2) {
+ // Drop the new connection, we can't use it.
+ //
+ conn = nullptr;
+ if (message) {
+ aListener->OnLDAPMessage(message);
+ return NS_OK;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!aEntry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ // We made the connection, lets store it to the server entry,
+ // and also update the reverse lookup tables (for finding the
+ // server entry related to a particular connection).
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ aEntry->SetConnection(conn);
+ mConnections.Put(conn, aEntry);
+ }
+
+ // Setup the bind() operation.
+ //
+ operation = do_CreateInstance(kLDAPOperationCID, &rv);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = operation->Init(conn, this, nullptr);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ // Start a bind operation
+ //
+ // Here we need to support the password, see bug #75990
+ //
+ rv = operation->SimpleBind(password);
+ if (NS_FAILED(rv)) {
+ // Only pass along errors we are aware of
+ if ((rv == NS_ERROR_LDAP_ENCODING_ERROR) ||
+ (rv == NS_ERROR_FAILURE) ||
+ (rv == NS_ERROR_OUT_OF_MEMORY))
+ return rv;
+ else
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+/* AString createFilter (in unsigned long aMaxSize, in AString aPattern, in AString aPrefix, in AString aSuffix, in AString aAttr, in AString aValue); */
+NS_IMETHODIMP nsLDAPService::CreateFilter(uint32_t aMaxSize,
+ const nsACString & aPattern,
+ const nsACString & aPrefix,
+ const nsACString & aSuffix,
+ const nsACString & aAttr,
+ const nsACString & aValue,
+ nsACString & _retval)
+{
+ if (!aMaxSize) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // figure out how big of an array we're going to need for the tokens,
+ // including a trailing NULL, and allocate space for it.
+ //
+ const char *iter = aValue.BeginReading();
+ const char *iterEnd = aValue.EndReading();
+ uint32_t numTokens = CountTokens(iter, iterEnd);
+ char **valueWords;
+ valueWords = static_cast<char **>(moz_xmalloc((numTokens + 1) *
+ sizeof(char *)));
+ if (!valueWords) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // build the array of values
+ //
+ uint32_t curToken = 0;
+ while (iter != iterEnd && curToken < numTokens ) {
+ valueWords[curToken] = NextToken(&iter, &iterEnd);
+ if ( !valueWords[curToken] ) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(curToken, valueWords);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ curToken++;
+ }
+ valueWords[numTokens] = 0; // end of array signal to LDAP C SDK
+
+ // make buffer to be used for construction
+ //
+ char *buffer = static_cast<char *>(moz_xmalloc(aMaxSize * sizeof(char)));
+ if (!buffer) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // create the filter itself
+ //
+ nsresult rv;
+ int result = ldap_create_filter(buffer, aMaxSize,
+ const_cast<char *>(PromiseFlatCString(aPattern).get()),
+ const_cast<char *>(PromiseFlatCString(aPrefix).get()),
+ const_cast<char *>(PromiseFlatCString(aSuffix).get()),
+ const_cast<char *>(PromiseFlatCString(aAttr).get()),
+ const_cast<char *>(PromiseFlatCString(aValue).get()),
+ valueWords);
+ switch (result) {
+ case LDAP_SUCCESS:
+ rv = NS_OK;
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPService::CreateFilter(): "
+ "filter longer than max size of %d generated",
+ aMaxSize));
+ rv = NS_ERROR_NOT_AVAILABLE;
+ break;
+
+ case LDAP_PARAM_ERROR:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+
+ default:
+ NS_ERROR("nsLDAPService::CreateFilter(): ldap_create_filter() "
+ "returned unexpected error");
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ _retval.Assign(buffer);
+
+ // done with the array and the buffer
+ //
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
+ free(buffer);
+
+ return rv;
+}
+
+// Parse a distinguished name (DN) and returns the relative DN,
+// base DN and the list of attributes that make up the relative DN.
+NS_IMETHODIMP nsLDAPService::ParseDn(const char *aDn,
+ nsACString &aRdn,
+ nsACString &aBaseDn,
+ uint32_t *aRdnCount,
+ char ***aRdnAttrs)
+{
+ NS_ENSURE_ARG_POINTER(aRdnCount);
+ NS_ENSURE_ARG_POINTER(aRdnAttrs);
+
+ // explode the DN
+ char **dnComponents = ldap_explode_dn(aDn, 0);
+ if (!dnComponents) {
+ NS_ERROR("nsLDAPService::ParseDn: parsing DN failed");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // count DN components
+ if (!*dnComponents || !*(dnComponents + 1)) {
+ NS_ERROR("nsLDAPService::ParseDn: DN has too few components");
+ ldap_value_free(dnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the base DN
+ nsAutoCString baseDn(nsDependentCString(*(dnComponents + 1)));
+ for (char **component = dnComponents + 2; *component; ++component) {
+ baseDn.AppendLiteral(",");
+ baseDn.Append(nsDependentCString(*component));
+ }
+
+ // explode the RDN
+ char **rdnComponents = ldap_explode_rdn(*dnComponents, 0);
+ if (!rdnComponents) {
+ NS_ERROR("nsLDAPService::ParseDn: parsing RDN failed");
+ ldap_value_free(dnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // count RDN attributes
+ uint32_t rdnCount = 0;
+ for (char **component = rdnComponents; *component; ++component)
+ ++rdnCount;
+ if (rdnCount < 1) {
+ NS_ERROR("nsLDAPService::ParseDn: RDN has too few components");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the RDN attribute names
+ char **attrNameArray = static_cast<char **>(
+ moz_xmalloc(rdnCount * sizeof(char *)));
+ if (!attrNameArray) {
+ NS_ERROR("nsLDAPService::ParseDn: out of memory ");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint32_t index = 0;
+ for (char **component = rdnComponents; *component; ++component) {
+ uint32_t len = 0;
+ char *p;
+ for (p = *component; *p != '\0' && *p != '='; ++p)
+ ++len;
+ if (*p != '=') {
+ NS_ERROR("nsLDAPService::parseDn: "
+ "could not find '=' in RDN component");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (!(attrNameArray[index] = (char*)NS_Alloc(len + 1))) {
+ NS_ERROR("nsLDAPService::ParseDn: out of memory ");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, attrNameArray);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memcpy(attrNameArray[index], *component, len);
+ *(attrNameArray[index] + len) = '\0';
+ ++index;
+ }
+
+ // perform assignments
+ aRdn.Assign(*dnComponents);
+ aBaseDn.Assign(baseDn);
+ *aRdnCount = rdnCount;
+ *aRdnAttrs = attrNameArray;
+
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_OK;
+}
+
+// Count the number of space-separated tokens between aIter and aIterEnd
+//
+uint32_t
+nsLDAPService::CountTokens(const char *aIter,
+ const char *aIterEnd)
+{
+ uint32_t count(0);
+
+ // keep iterating through the string until we hit the end
+ //
+ while (aIter != aIterEnd) {
+
+ // move past any leading spaces
+ //
+ while (aIter != aIterEnd &&
+ ldap_utf8isspace(const_cast<char *>(aIter))){
+ ++aIter;
+ }
+
+ // move past all chars in this token
+ //
+ while (aIter != aIterEnd) {
+
+ if (ldap_utf8isspace(const_cast<char *>(aIter))) {
+ ++count; // token finished; increment the count
+ ++aIter; // move past the space
+ break;
+ }
+
+ ++aIter; // move to next char
+
+ // if we've hit the end of this token and the end of this
+ // iterator simultaneous, be sure to bump the count, since we're
+ // never gonna hit the IsAsciiSpace where it's normally done.
+ //
+ if (aIter == aIterEnd) {
+ ++count;
+ }
+
+ }
+ }
+
+ return count;
+}
+
+// return the next token in this iterator
+//
+char*
+nsLDAPService::NextToken(const char **aIter,
+ const char **aIterEnd)
+{
+ // move past any leading whitespace
+ //
+ while (*aIter != *aIterEnd &&
+ ldap_utf8isspace(const_cast<char *>(*aIter))) {
+ ++(*aIter);
+ }
+
+ const char *start = *aIter;
+
+ // copy the token into our local variable
+ //
+ while (*aIter != *aIterEnd &&
+ !ldap_utf8isspace(const_cast<char *>(*aIter))) {
+ ++(*aIter);
+ }
+
+ return ToNewCString(Substring(start, *aIter));
+}
diff --git a/ldap/xpcom/src/nsLDAPService.h b/ldap/xpcom/src/nsLDAPService.h
new file mode 100644
index 000000000..2b0f4ef4e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPService.h
@@ -0,0 +1,116 @@
+/* -*- 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 "ldap.h"
+#include "nsStringGlue.h"
+#include "nsCOMArray.h"
+#include "nsDataHashtable.h"
+#include "nsILDAPService.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPMessageListener.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPServer.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPMessage.h"
+#include "mozilla/Mutex.h"
+
+// 6a89ae33-7a90-430d-888c-0dede53a951a
+//
+#define NS_LDAPSERVICE_CID \
+{ \
+ 0x6a89ae33, 0x7a90, 0x430d, \
+ {0x88, 0x8c, 0x0d, 0xed, 0xe5, 0x3a, 0x95, 0x1a} \
+}
+
+// This is a little "helper" class, we use to store information
+// related to one Service entry (one LDAP server).
+//
+class nsLDAPServiceEntry
+{
+ public:
+ nsLDAPServiceEntry();
+ virtual ~nsLDAPServiceEntry() {};
+ bool Init();
+
+ inline uint32_t GetLeases();
+ inline void IncrementLeases();
+ inline bool DecrementLeases();
+
+ inline PRTime GetTimestamp();
+ inline void SetTimestamp();
+
+ inline already_AddRefed<nsILDAPServer> GetServer();
+ inline bool SetServer(nsILDAPServer *aServer);
+
+ inline already_AddRefed<nsILDAPConnection> GetConnection();
+ inline void SetConnection(nsILDAPConnection *aConnection);
+
+ inline already_AddRefed<nsILDAPMessage> GetMessage();
+ inline void SetMessage(nsILDAPMessage *aMessage);
+
+ inline already_AddRefed<nsILDAPMessageListener> PopListener();
+ inline bool PushListener(nsILDAPMessageListener *);
+
+ inline bool IsRebinding();
+ inline void SetRebinding(bool);
+
+ inline bool DeleteEntry();
+
+ protected:
+ uint32_t mLeases; // The number of leases currently granted
+ PRTime mTimestamp; // Last time this server was "used"
+ bool mDelete; // This entry is due for deletion
+ bool mRebinding; // Keep state if we are rebinding or not
+
+ nsCOMPtr<nsILDAPServer> mServer;
+ nsCOMPtr<nsILDAPConnection> mConnection;
+ nsCOMPtr<nsILDAPMessage> mMessage;
+
+ // Array holding all the pending callbacks (listeners) for this entry
+ nsCOMArray<nsILDAPMessageListener> mListeners;
+};
+
+// This is the interface we're implementing.
+//
+class nsLDAPService : public nsILDAPService, public nsILDAPMessageListener
+{
+ public:
+ // interface decls
+ //
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPSERVICE
+ NS_DECL_NSILDAPMESSAGELISTENER
+
+ // constructor and destructor
+ //
+ nsLDAPService();
+
+ nsresult Init();
+
+ protected:
+ virtual ~nsLDAPService();
+ nsresult EstablishConnection(nsLDAPServiceEntry *,
+ nsILDAPMessageListener *);
+
+ // kinda like strtok_r, but with iterators. for use by
+ // createFilter
+ //
+ char *NextToken(const char **aIter, const char **aIterEnd);
+
+ // count how many tokens are in this string; for use by
+ // createFilter; note that unlike with NextToken, these params
+ // are copies, not references.
+ //
+ uint32_t CountTokens(const char * aIter, const char * aIterEnd);
+
+
+ mozilla::Mutex mLock; // Lock mechanism
+
+ // Hash table holding server entries
+ nsDataHashtable<nsStringHashKey, nsLDAPServiceEntry*> mServers;
+ // Hash table holding "reverse" lookups from connection to server
+ nsDataHashtable<nsVoidPtrHashKey, nsLDAPServiceEntry*> mConnections;
+};
diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.cpp b/ldap/xpcom/src/nsLDAPSyncQuery.cpp
new file mode 100644
index 000000000..e1f70ee7f
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSyncQuery.cpp
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsLDAPSyncQuery.h"
+#include "nsIServiceManager.h"
+#include "nsILDAPErrors.h"
+#include "nsThreadUtils.h"
+#include "nsILDAPMessage.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemory.h"
+
+// nsISupports Implementation
+
+NS_IMPL_ISUPPORTS(nsLDAPSyncQuery, nsILDAPSyncQuery, nsILDAPMessageListener)
+
+// Constructor
+//
+nsLDAPSyncQuery::nsLDAPSyncQuery() :
+ mFinished(false), // This is a control variable for event loop
+ mProtocolVersion(nsILDAPConnection::VERSION3)
+{
+}
+
+// Destructor
+//
+nsLDAPSyncQuery::~nsLDAPSyncQuery()
+{
+}
+
+
+// Messages received are passed back via this function.
+// void OnLDAPMessage (in nsILDAPMessage aMessage)
+//
+NS_IMETHODIMP
+nsLDAPSyncQuery::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ int32_t messageType;
+
+ // just in case.
+ //
+ if (!aMessage) {
+ return NS_OK;
+ }
+
+ // figure out what sort of message was returned
+ //
+ nsresult rv = aMessage->GetType(&messageType);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected "
+ "error in aMessage->GetType()");
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ switch (messageType) {
+
+ case nsILDAPMessage::RES_BIND:
+
+ // a bind has completed
+ //
+ return OnLDAPBind(aMessage);
+
+ case nsILDAPMessage::RES_SEARCH_ENTRY:
+
+ // a search entry has been returned
+ //
+ return OnLDAPSearchEntry(aMessage);
+
+ case nsILDAPMessage::RES_SEARCH_RESULT:
+
+ // the search is finished; we're all done
+ //
+ return OnLDAPSearchResult(aMessage);
+
+ default:
+
+ // Given the LDAP operations nsLDAPSyncQuery uses, we should
+ // never get here. If we do get here in a release build, it's
+ // probably a bug, but maybe it's the LDAP server doing something
+ // weird. Might as well try and continue anyway. The session should
+ // eventually get reaped by the timeout code, if necessary.
+ //
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected "
+ "LDAP message received");
+ return NS_OK;
+ }
+}
+
+// void onLDAPInit (in nsresult aStatus);
+//
+NS_IMETHODIMP
+nsLDAPSyncQuery::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
+{
+ nsresult rv; // temp for xpcom return values
+ // create and initialize an LDAP operation (to be used for the bind)
+ //
+ mOperation = do_CreateInstance("@mozilla.org/network/ldap-operation;1",
+ &rv);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // our OnLDAPMessage accepts all result callbacks
+ //
+ rv = mOperation->Init(mConnection, this, nullptr);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ // kick off a bind operation
+ //
+ rv = mOperation->SimpleBind(EmptyCString());
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsLDAPSyncQuery::OnLDAPBind(nsILDAPMessage *aMessage)
+{
+
+ int32_t errCode;
+
+ mOperation = nullptr; // done with bind op; make nsCOMPtr release it
+
+ // get the status of the bind
+ //
+ nsresult rv = aMessage->GetErrorCode(&errCode);
+ if (NS_FAILED(rv)) {
+
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPBind(): couldn't get "
+ "error code from aMessage");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+
+ // check to be sure the bind succeeded
+ //
+ if (errCode != nsILDAPErrors::SUCCESS) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // ok, we're starting a search
+ //
+ return StartLDAPSearch();
+}
+
+nsresult
+nsLDAPSyncQuery::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
+{
+ uint32_t attrCount;
+ char** attributes;
+ nsresult rv = aMessage->GetAttributes(&attrCount, &attributes);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): "
+ "aMessage->GetAttributes() failed");
+ FinishLDAPQuery();
+ return rv;
+ }
+
+ // Iterate through the attributes received in this message
+ for (uint32_t i = 0; i < attrCount; i++)
+ {
+ char16_t **vals;
+ uint32_t valueCount;
+
+ // Get the values of this attribute.
+ // XXX better failure handling
+ rv = aMessage->GetValues(attributes[i], &valueCount, &vals);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): "
+ "aMessage->GetValues() failed\n");
+ FinishLDAPQuery();
+ break;
+ }
+
+ // Store all values of this attribute in the mResults.
+ for (uint32_t j = 0; j < valueCount; j++) {
+ mResults.Append(char16_t('\n'));
+ mResults.AppendASCII(attributes[i]);
+ mResults.Append(char16_t('='));
+ mResults.Append(vals[j]);
+ }
+
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(valueCount, vals);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(attrCount, attributes);
+
+ return rv;
+}
+
+
+nsresult
+nsLDAPSyncQuery::OnLDAPSearchResult(nsILDAPMessage *aMessage)
+{
+ // We are done with the LDAP search.
+ // Release the control variable for the eventloop and other members
+ //
+ FinishLDAPQuery();
+ return NS_OK;
+}
+
+nsresult
+nsLDAPSyncQuery::StartLDAPSearch()
+{
+ nsresult rv;
+ // create and initialize an LDAP operation (to be used for the search
+ //
+ mOperation =
+ do_CreateInstance("@mozilla.org/network/ldap-operation;1", &rv);
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't "
+ "create @mozilla.org/network/ldap-operation;1");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // initialize the LDAP operation object
+ //
+ rv = mOperation->Init(mConnection, this, nullptr);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't "
+ "initialize LDAP operation");
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the search filter associated with the directory server url;
+ //
+ nsAutoCString urlFilter;
+ rv = mServerURL->GetFilter(urlFilter);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the base dn to search
+ //
+ nsAutoCString dn;
+ rv = mServerURL->GetDn(dn);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // and the scope
+ //
+ int32_t scope;
+ rv = mServerURL->GetScope(&scope);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoCString attributes;
+ rv = mServerURL->GetAttributes(attributes);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // time to kick off the search.
+ rv = mOperation->SearchExt(dn, scope, urlFilter, attributes, 0, 0);
+
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// void initConnection ();
+//
+nsresult nsLDAPSyncQuery::InitConnection()
+{
+ // Because mConnection->Init proxies back to the main thread, this
+ // better be the main thread.
+ NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE);
+ nsresult rv; // temp for xpcom return values
+ // create an LDAP connection
+ //
+ mConnection = do_CreateInstance("@mozilla.org/network/ldap-connection;1",
+ &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::InitConnection(): could "
+ "not create @mozilla.org/network/ldap-connection;1");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // have we been properly initialized?
+ //
+ if (!mServerURL) {
+ NS_ERROR("nsLDAPSyncQuery::InitConnection(): mServerURL "
+ "is NULL");
+ FinishLDAPQuery();
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ rv = mConnection->Init(mServerURL, EmptyCString(), this,
+ nullptr, mProtocolVersion);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ return NS_OK;
+}
+
+void
+nsLDAPSyncQuery::FinishLDAPQuery()
+{
+ // We are done with the LDAP operation.
+ // Release the Control variable for the eventloop
+ //
+ mFinished = true;
+
+ // Release member variables
+ //
+ mConnection = nullptr;
+ mOperation = nullptr;
+ mServerURL = nullptr;
+
+}
+
+/* wstring getQueryResults (in nsILDAPURL aServerURL, in unsigned long aVersion); */
+NS_IMETHODIMP nsLDAPSyncQuery::GetQueryResults(nsILDAPURL *aServerURL,
+ uint32_t aProtocolVersion,
+ char16_t **_retval)
+{
+ nsresult rv;
+
+ if (!aServerURL) {
+ NS_ERROR("nsLDAPSyncQuery::GetQueryResults() called without LDAP URL");
+ return NS_ERROR_FAILURE;
+ }
+ mServerURL = aServerURL;
+ mProtocolVersion = aProtocolVersion;
+
+ nsCOMPtr<nsIThread> currentThread = do_GetCurrentThread();
+
+ // Start an LDAP query.
+ // InitConnection will bind to the ldap server and post a OnLDAPMessage
+ // event. This event will trigger a search and the whole operation will
+ // be carried out by chain of events
+ //
+ rv = InitConnection();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We want this LDAP query to be synchronous while the XPCOM LDAP is
+ // async in nature. So this eventQueue handling will wait for the
+ // LDAP operation to be finished.
+ // mFinished controls the state of the LDAP opertion.
+ // It will be released in any case (success/failure)
+
+
+ // Run the event loop,
+ // mFinished is a control variable
+ //
+ while (!mFinished)
+ NS_ENSURE_STATE(NS_ProcessNextEvent(currentThread));
+
+ // Return results
+ //
+ if (!mResults.IsEmpty()) {
+ *_retval = ToNewUnicode(mResults);
+ if (!_retval)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+
+}
diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.h b/ldap/xpcom/src/nsLDAPSyncQuery.h
new file mode 100644
index 000000000..78cfd43a4
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSyncQuery.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsCOMPtr.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsILDAPMessageListener.h"
+#include "nsILDAPURL.h"
+#include "nsStringGlue.h"
+#include "nsILDAPSyncQuery.h"
+
+// DDDEE14E-ED81-4182-9323-C2AB22FBA68E
+#define NS_LDAPSYNCQUERY_CID \
+{ 0xdddee14e, 0xed81, 0x4182, \
+ { 0x93, 0x23, 0xc2, 0xab, 0x22, 0xfb, 0xa6, 0x8e }}
+
+
+class nsLDAPSyncQuery : public nsILDAPSyncQuery,
+ public nsILDAPMessageListener
+
+{
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMESSAGELISTENER
+ NS_DECL_NSILDAPSYNCQUERY
+
+ nsLDAPSyncQuery();
+
+ protected:
+ virtual ~nsLDAPSyncQuery();
+
+ nsCOMPtr<nsILDAPConnection> mConnection; // connection used for search
+ nsCOMPtr<nsILDAPOperation> mOperation; // current ldap op
+ nsCOMPtr<nsILDAPURL> mServerURL; // LDAP URL
+ bool mFinished; // control variable for eventQ
+ nsString mResults; // values to return
+ uint32_t mProtocolVersion; // LDAP version to use
+
+ nsresult InitConnection();
+ // check that we bound ok and start then call StartLDAPSearch
+ nsresult OnLDAPBind(nsILDAPMessage *aMessage);
+
+ // add to the results set
+ nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage);
+
+
+ nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage);
+
+ // kick off a search
+ nsresult StartLDAPSearch();
+
+ // Clean up after the LDAP Query is done.
+ void FinishLDAPQuery();
+};
+
diff --git a/ldap/xpcom/src/nsLDAPURL.cpp b/ldap/xpcom/src/nsLDAPURL.cpp
new file mode 100644
index 000000000..752ee5b3b
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPURL.cpp
@@ -0,0 +1,750 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "nsLDAPURL.h"
+#include "netCore.h"
+#include "plstr.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIStandardURL.h"
+#include "nsMsgUtils.h"
+
+// The two schemes we support, LDAP and LDAPS
+//
+NS_NAMED_LITERAL_CSTRING(LDAP_SCHEME, "ldap");
+NS_NAMED_LITERAL_CSTRING(LDAP_SSL_SCHEME, "ldaps");
+
+NS_IMPL_ISUPPORTS(nsLDAPURL, nsILDAPURL, nsIURI)
+
+nsLDAPURL::nsLDAPURL()
+ : mScope(SCOPE_BASE),
+ mOptions(0)
+{
+}
+
+nsLDAPURL::~nsLDAPURL()
+{
+}
+
+nsresult
+nsLDAPURL::Init(uint32_t aUrlType, int32_t aDefaultPort,
+ const nsACString &aSpec, const char* aOriginCharset,
+ nsIURI *aBaseURI)
+{
+ if (!mBaseURL)
+ {
+ mBaseURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
+ if (!mBaseURL)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIStandardURL> standardURL(do_QueryInterface(mBaseURL, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = standardURL->Init(aUrlType, aDefaultPort, aSpec, aOriginCharset,
+ aBaseURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now get the spec from the mBaseURL in case it was a relative one
+ nsCString spec;
+ rv = mBaseURL->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SetSpec(spec);
+}
+
+void
+nsLDAPURL::GetPathInternal(nsCString &aPath)
+{
+ aPath.Assign('/');
+
+ if (!mDN.IsEmpty())
+ aPath.Append(mDN);
+
+ if (!mAttributes.IsEmpty())
+ aPath.Append('?');
+
+ // If mAttributes isn't empty, cut off the internally stored commas at start
+ // and end, and append to the path.
+ if (!mAttributes.IsEmpty())
+ aPath.Append(Substring(mAttributes, 1, mAttributes.Length() - 2));
+
+ if (mScope || !mFilter.IsEmpty())
+ {
+ aPath.Append((mAttributes.IsEmpty() ? "??" : "?"));
+ if (mScope)
+ {
+ if (mScope == SCOPE_ONELEVEL)
+ aPath.Append("one");
+ else if (mScope == SCOPE_SUBTREE)
+ aPath.Append("sub");
+ }
+ if (!mFilter.IsEmpty())
+ {
+ aPath.Append('?');
+ aPath.Append(mFilter);
+ }
+ }
+}
+
+nsresult
+nsLDAPURL::SetPathInternal(const nsCString &aPath)
+{
+ LDAPURLDesc *desc;
+
+ // This is from the LDAP C-SDK, which currently doesn't
+ // support everything from RFC 2255... :(
+ //
+ int err = ldap_url_parse(aPath.get(), &desc);
+ switch (err) {
+ case LDAP_SUCCESS: {
+ // The base URL can pick up the host & port details and deal with them
+ // better than we can
+ mDN = desc->lud_dn;
+ mScope = desc->lud_scope;
+ mFilter = desc->lud_filter;
+ mOptions = desc->lud_options;
+ nsresult rv = SetAttributeArray(desc->lud_attrs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ ldap_free_urldesc(desc);
+ return NS_OK;
+ }
+
+ case LDAP_URL_ERR_NOTLDAP:
+ case LDAP_URL_ERR_NODN:
+ case LDAP_URL_ERR_BADSCOPE:
+ return NS_ERROR_MALFORMED_URI;
+
+ case LDAP_URL_ERR_MEM:
+ NS_ERROR("nsLDAPURL::SetSpec: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ case LDAP_URL_ERR_PARAM:
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // This shouldn't happen...
+ return NS_ERROR_UNEXPECTED;
+}
+
+// A string representation of the URI. Setting the spec
+// causes the new spec to be parsed, initializing the URI. Setting
+// the spec (or any of the accessors) causes also any currently
+// open streams on the URI's channel to be closed.
+
+NS_IMETHODIMP
+nsLDAPURL::GetSpec(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetSpec(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetSpec(const nsACString &aSpec)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Cache the original spec in case we don't like what we've been passed and
+ // need to reset ourselves.
+ nsCString originalSpec;
+ nsresult rv = mBaseURL->GetSpec(originalSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mBaseURL->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetPathInternal(PromiseFlatCString(aSpec));
+ if (NS_FAILED(rv))
+ mBaseURL->SetSpec(originalSpec);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsLDAPURL::GetPrePath(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPrePath(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetScheme(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetScheme(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetScheme(const nsACString &aScheme)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (aScheme.Equals(LDAP_SCHEME, nsCaseInsensitiveCStringComparator()))
+ mOptions &= !OPT_SECURE;
+ else if (aScheme.Equals(LDAP_SSL_SCHEME,
+ nsCaseInsensitiveCStringComparator()))
+ mOptions |= OPT_SECURE;
+ else
+ return NS_ERROR_MALFORMED_URI;
+
+ return mBaseURL->SetScheme(aScheme);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetUserPass(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetUserPass(const nsACString &aUserPass)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetUsername(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetUsername(const nsACString &aUsername)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetPassword(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetPassword(const nsACString &aPassword)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHostPort(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetHostPort(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHostPort(const nsACString &aHostPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHostAndPort(const nsACString &aHostPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHostAndPort(aHostPort);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHost(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetHost(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHost(const nsACString &aHost)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHost(aHost);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetPort(int32_t *_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPort(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetPort(int32_t aPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetPort(aPort);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetPath(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPath(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetPath(const nsACString &aPath)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = SetPathInternal(PromiseFlatCString(aPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mBaseURL->SetPath(aPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAsciiSpec(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // XXX handle extra items?
+ return mBaseURL->GetAsciiSpec(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAsciiHost(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetAsciiHost(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetAsciiHostPort(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetAsciiHostPort(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetOriginCharset(nsACString &result)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetOriginCharset(result);
+}
+
+// boolean equals (in nsIURI other)
+// (based on nsSimpleURI::Equals)
+NS_IMETHODIMP nsLDAPURL::Equals(nsIURI *other, bool *_retval)
+{
+ *_retval = false;
+ if (other)
+ {
+ nsresult rv;
+ nsCOMPtr<nsILDAPURL> otherURL(do_QueryInterface(other, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString thisSpec, otherSpec;
+ uint32_t otherOptions;
+
+ rv = GetSpec(thisSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = otherURL->GetSpec(otherSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = otherURL->GetOptions(&otherOptions);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (thisSpec == otherSpec && mOptions == otherOptions)
+ *_retval = true;
+ }
+ }
+ return NS_OK;
+}
+
+// boolean schemeIs(in const char * scheme);
+//
+NS_IMETHODIMP nsLDAPURL::SchemeIs(const char *aScheme, bool *aEquals)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SchemeIs(aScheme, aEquals);
+}
+
+// nsIURI clone ();
+//
+nsresult
+nsLDAPURL::CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsLDAPURL *clone = new nsLDAPURL();
+
+ if (!clone)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ clone->mDN = mDN;
+ clone->mScope = mScope;
+ clone->mFilter = mFilter;
+ clone->mOptions = mOptions;
+ clone->mAttributes = mAttributes;
+
+ nsresult rv;
+ if (aRefHandlingMode == eHonorRef) {
+ rv = mBaseURL->Clone(getter_AddRefs(clone->mBaseURL));
+ } else if (aRefHandlingMode == eReplaceRef) {
+ rv = mBaseURL->CloneWithNewRef(newRef, getter_AddRefs(clone->mBaseURL));
+ } else {
+ rv = mBaseURL->CloneIgnoringRef(getter_AddRefs(clone->mBaseURL));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = clone);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::Clone(nsIURI** result)
+{
+ return CloneInternal(eHonorRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::CloneIgnoringRef(nsIURI** result)
+{
+ return CloneInternal(eIgnoreRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::CloneWithNewRef(const nsACString& newRef, nsIURI** result)
+{
+ return CloneInternal(eReplaceRef, newRef, result);
+}
+
+// string resolve (in string relativePath);
+//
+NS_IMETHODIMP nsLDAPURL::Resolve(const nsACString &relativePath,
+ nsACString &_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// The following attributes come from nsILDAPURL
+
+// attribute AUTF8String dn;
+//
+NS_IMETHODIMP nsLDAPURL::GetDn(nsACString& _retval)
+{
+ _retval.Assign(mDN);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPURL::SetDn(const nsACString& aDn)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ mDN.Assign(aDn);
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAttributes(nsACString &aAttributes)
+{
+ if (mAttributes.IsEmpty())
+ {
+ aAttributes.Truncate();
+ return NS_OK;
+ }
+
+ NS_ASSERTION(mAttributes[0] == ',' &&
+ mAttributes[mAttributes.Length() - 1] == ',',
+ "mAttributes does not begin and end with a comma");
+
+ // We store the string internally with comma before and after, so strip
+ // them off here.
+ aAttributes = Substring(mAttributes, 1, mAttributes.Length() - 2);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetAttributes(const nsACString &aAttributes)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (aAttributes.IsEmpty())
+ mAttributes.Truncate();
+ else
+ {
+ // We need to make sure we start off the string with a comma.
+ if (aAttributes[0] != ',')
+ mAttributes = ',';
+
+ mAttributes.Append(aAttributes);
+
+ // Also end with a comma if appropriate.
+ if (mAttributes[mAttributes.Length() - 1] != ',')
+ mAttributes.Append(',');
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+nsresult nsLDAPURL::SetAttributeArray(char** aAttributes)
+{
+ mAttributes.Truncate();
+
+ while (aAttributes && *aAttributes)
+ {
+ // Always start with a comma as that's what we store internally.
+ mAttributes.Append(',');
+ mAttributes.Append(*aAttributes);
+ ++aAttributes;
+ }
+
+ // Add a comma on the end if we have something.
+ if (!mAttributes.IsEmpty())
+ mAttributes.Append(',');
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::AddAttribute(const nsACString &aAttribute)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mAttributes.IsEmpty())
+ {
+ mAttributes = ',';
+ mAttributes.Append(aAttribute);
+ mAttributes.Append(',');
+ }
+ else
+ {
+ // Wrap the attribute in commas, so that we can do an exact match.
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ // Check to see if the attribute is already stored. If it is, then also
+ // check to see if it is the last attribute in the string, or if the next
+ // character is a comma, this means we won't match substrings.
+ int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare);
+ if (pos != -1)
+ return NS_OK;
+
+ mAttributes.Append(Substring(findAttribute, 1));
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::RemoveAttribute(const nsACString &aAttribute)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mAttributes.IsEmpty())
+ return NS_OK;
+
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ if (mAttributes.Equals(findAttribute, nsCaseInsensitiveCStringComparator()))
+ mAttributes.Truncate();
+ else
+ {
+ int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare);
+ if (pos == -1)
+ return NS_OK;
+
+ mAttributes.Cut(pos, findAttribute.Length() - 1);
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::HasAttribute(const nsACString &aAttribute,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ *_retval = mAttributes.Find(findAttribute, CaseInsensitiveCompare) != -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::GetScope(int32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mScope;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetScope(int32_t aScope)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Only allow scopes supported by the C-SDK
+ if ((aScope != SCOPE_BASE) && (aScope != SCOPE_ONELEVEL) &&
+ (aScope != SCOPE_SUBTREE))
+ return NS_ERROR_MALFORMED_URI;
+
+ mScope = aScope;
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetFilter(nsACString& _retval)
+{
+ _retval.Assign(mFilter);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPURL::SetFilter(const nsACString& aFilter)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ mFilter.Assign(aFilter);
+
+ if (mFilter.IsEmpty())
+ mFilter.AssignLiteral("(objectclass=*)");
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetOptions(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mOptions;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetOptions(uint32_t aOptions)
+{
+ // Secure is the only option supported at the moment
+ if ((mOptions & OPT_SECURE) == (aOptions & OPT_SECURE))
+ return NS_OK;
+
+ mOptions = aOptions;
+
+ if ((aOptions & OPT_SECURE) == OPT_SECURE)
+ return SetScheme(LDAP_SSL_SCHEME);
+
+ return SetScheme(LDAP_SCHEME);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetRef(const nsACString &aRef)
+{
+ return mBaseURL->SetRef(aRef);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetRef(nsACString &result)
+{
+ return mBaseURL->GetRef(result);
+}
+
+NS_IMETHODIMP nsLDAPURL::EqualsExceptRef(nsIURI *other, bool *result)
+{
+ return mBaseURL->EqualsExceptRef(other, result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetSpecIgnoringRef(nsACString &result)
+{
+ return mBaseURL->GetSpecIgnoringRef(result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHasRef(bool *result)
+{
+ return mBaseURL->GetHasRef(result);
+}
+
+
+NS_IMETHODIMP
+nsLDAPURL::GetFilePath(nsACString &aFilePath)
+{
+ return mBaseURL->GetFilePath(aFilePath);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetFilePath(const nsACString &aFilePath)
+{
+ return mBaseURL->SetFilePath(aFilePath);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetQuery(nsACString &aQuery)
+{
+ return mBaseURL->GetQuery(aQuery);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetQuery(const nsACString &aQuery)
+{
+ return mBaseURL->SetQuery(aQuery);
+} \ No newline at end of file
diff --git a/ldap/xpcom/src/nsLDAPURL.h b/ldap/xpcom/src/nsLDAPURL.h
new file mode 100644
index 000000000..b80da957d
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPURL.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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 "ldap.h"
+#include "nsStringGlue.h"
+#include "nsILDAPURL.h"
+#include "nsCOMPtr.h"
+
+// cb7c67f8-0053-4072-89e9-501cbd1b35ab
+#define NS_LDAPURL_CID \
+{ 0xcb7c67f8, 0x0053, 0x4072, \
+ { 0x89, 0xe9, 0x50, 0x1c, 0xbd, 0x1b, 0x35, 0xab}}
+
+/**
+ * nsLDAPURL
+ *
+ * nsLDAPURL uses an nsStandardURL stored in mBaseURL as its main url formatter.
+ *
+ * This is done to ensure that the pre-path sections of the URI are correctly
+ * formatted and to re-use the functions for nsIURI as appropriate.
+ *
+ * Handling of the path sections of the URI are done within nsLDAPURL/parts of
+ * the LDAP c-sdk. nsLDAPURL holds the individual sections of the path of the
+ * URI locally (to allow convenient get/set), but always updates the mBaseURL
+ * when one changes to ensure that mBaseURL.spec and the local data are kept
+ * consistent.
+ */
+
+class nsLDAPURL : public nsILDAPURL
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSILDAPURL
+
+ nsLDAPURL();
+
+protected:
+ enum RefHandlingEnum {
+ eIgnoreRef,
+ eHonorRef,
+ eReplaceRef
+ };
+ virtual ~nsLDAPURL();
+
+ void GetPathInternal(nsCString &aPath);
+ nsresult SetPathInternal(const nsCString &aPath);
+ nsresult SetAttributeArray(char** aAttributes);
+ nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** aResult);
+
+ nsCString mDN; // Base Distinguished Name (Base DN)
+ int32_t mScope; // Search scope (base, one or sub)
+ nsCString mFilter; // LDAP search filter
+ uint32_t mOptions; // Options
+ nsCString mAttributes;
+ nsCOMPtr<nsIURI> mBaseURL;
+};
diff --git a/ldap/xpcom/src/nsLDAPUtils.h b/ldap/xpcom/src/nsLDAPUtils.h
new file mode 100644
index 000000000..87f5dacc1
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPUtils.h
@@ -0,0 +1,147 @@
+/* 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/. */
+
+/* This module contains helper functions and macros for converting directory
+ module to frozen linkage.
+ */
+#include "nsServiceManagerUtils.h"
+#include "nsStringGlue.h"
+#include <ctype.h>
+
+#ifdef MOZILLA_INTERNAL_API
+/* Internal API helper macros */
+
+#define LdapCompressWhitespace(str) \
+ (str).CompressWhitespace()
+
+#else // MOZILLA_INTERNAL_API
+/* Frozen linkage helper functions */
+
+/* This macro has been copied from msgcore.h */
+#define IS_SPACE(VAL) \
+ (((((PRIntn)(VAL)) & 0x7f) == ((PRIntn)(VAL))) && isspace((PRIntn)(VAL)))
+
+/* This function has been copied from nsMsgUtils.cpp */
+inline void LdapCompressWhitespace(nsCString& aString)
+{
+ // This code is frozen linkage specific
+ aString.Trim(" \f\n\r\t\v");
+
+ char *start, *end;
+ aString.BeginWriting(&start, &end);
+
+ for (char *cur = start; cur < end; ++cur) {
+ if (!IS_SPACE(*cur))
+ continue;
+
+ *cur = ' ';
+
+ if (!IS_SPACE(*(cur + 1)))
+ continue;
+
+ // Loop through the white space
+ char *wend = cur + 2;
+ while (IS_SPACE(*wend))
+ ++wend;
+
+ uint32_t wlen = wend - cur - 1;
+
+ // fix "end"
+ end -= wlen;
+
+ // move everything forwards a bit
+ for (char *m = cur + 1; m < end; ++m) {
+ *m = *(m + wlen);
+ }
+ }
+
+ // Set the new length.
+ aString.SetLength(end - start);
+}
+
+/*
+ * Function copied from nsReadableUtils.
+ * Migrating to frozen linkage is the only change done
+ */
+inline
+bool IsUTF8(const nsACString& aString)
+{
+ const char *done_reading = aString.EndReading();
+
+ int32_t state = 0;
+ bool overlong = false;
+ bool surrogate = false;
+ bool nonchar = false;
+ uint16_t olupper = 0; // overlong byte upper bound.
+ uint16_t slower = 0; // surrogate byte lower bound.
+
+ const char *ptr = aString.BeginReading();
+
+ while (ptr < done_reading) {
+ uint8_t c;
+
+ if (0 == state) {
+
+ c = *ptr++;
+
+ if ((c & 0x80) == 0x00)
+ continue;
+
+ if ( c <= 0xC1 ) // [80-BF] where not expected, [C0-C1] for overlong.
+ return false;
+ else if ((c & 0xE0) == 0xC0)
+ state = 1;
+ else if ((c & 0xF0) == 0xE0) {
+ state = 2;
+ if ( c == 0xE0 ) { // to exclude E0[80-9F][80-BF]
+ overlong = true;
+ olupper = 0x9F;
+ } else if ( c == 0xED ) { // ED[A0-BF][80-BF] : surrogate codepoint
+ surrogate = true;
+ slower = 0xA0;
+ } else if ( c == 0xEF ) // EF BF [BE-BF] : non-character
+ nonchar = true;
+ } else if ( c <= 0xF4 ) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
+ state = 3;
+ nonchar = true;
+ if ( c == 0xF0 ) { // to exclude F0[80-8F][80-BF]{2}
+ overlong = true;
+ olupper = 0x8F;
+ }
+ else if ( c == 0xF4 ) { // to exclude F4[90-BF][80-BF]
+ // actually not surrogates but codepoints beyond 0x10FFFF
+ surrogate = true;
+ slower = 0x90;
+ }
+ } else
+ return false; // Not UTF-8 string
+ }
+
+ while (ptr < done_reading && state) {
+ c = *ptr++;
+ --state;
+
+ // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+ if ( nonchar && ( !state && c < 0xBE ||
+ state == 1 && c != 0xBF ||
+ state == 2 && 0x0F != (0x0F & c) ))
+ nonchar = false;
+
+ if ((c & 0xC0) != 0x80 || overlong && c <= olupper ||
+ surrogate && slower <= c || nonchar && !state )
+ return false; // Not UTF-8 string
+ overlong = surrogate = false;
+ }
+ }
+ return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
+}
+
+#define kNotFound -1
+
+#define nsCaseInsensitiveCStringComparator() \
+ CaseInsensitiveCompare
+#define nsCaseInsensitiveStringComparator() \
+ CaseInsensitiveCompare
+
+#endif // MOZILLA_INTERNAL_API