Bug 419595 nsLDAPURL implementation is incorrect. r=bienvenu,sr=dmose
authorMark Banner <bugzilla@standard8.plus.com>
Tue, 13 Jan 2009 10:37:12 +0000
changeset 1624 f13a45c4f1f912730f5313bcef509dc25502c6d3
parent 1623 306be9e163b0b3a26e9b225a234616790f8df844
child 1625 a048b5420d62dea34f3d8ee230922a6ea736353b
push id1300
push userbugzilla@standard8.plus.com
push dateTue, 13 Jan 2009 10:41:08 +0000
treeherdercomm-central@f13a45c4f1f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbienvenu, dmose
bugs419595
Bug 419595 nsLDAPURL implementation is incorrect. r=bienvenu,sr=dmose
directory/xpcom/Makefile.in
directory/xpcom/base/public/nsILDAPURL.idl
directory/xpcom/base/src/Makefile.in
directory/xpcom/base/src/nsLDAPChannel.h
directory/xpcom/base/src/nsLDAPProtocolHandler.cpp
directory/xpcom/base/src/nsLDAPProtocolHandler.h
directory/xpcom/base/src/nsLDAPProtocolHandler.js
directory/xpcom/base/src/nsLDAPProtocolModule.cpp
directory/xpcom/base/src/nsLDAPURL.cpp
directory/xpcom/base/src/nsLDAPURL.h
directory/xpcom/tests/Makefile.in
directory/xpcom/tests/jar.mn
directory/xpcom/tests/unit/test_nsLDAPURL.js
mailnews/addrbook/prefs/resources/content/pref-directory-add.js
mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl
mailnews/addrbook/src/Makefile.in
mailnews/addrbook/src/nsAbLDAPAttributeMap.js
mailnews/addrbook/src/nsAbLDAPDirectory.cpp
mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
mailnews/addrbook/test/unit/test_ldap1.js
--- a/directory/xpcom/Makefile.in
+++ b/directory/xpcom/Makefile.in
@@ -38,27 +38,24 @@
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-# Note that for the time being "tests" require LDAP Experimental
-#
 DIRS		= \
 		base \
 		$(NULL)
 
 ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
 DIRS		+= \
 		datasource \
 		$(NULL)
+endif
 
 ifdef ENABLE_TESTS
 DIRS		+= \
 		tests
 endif
 
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/directory/xpcom/base/public/nsILDAPURL.idl
+++ b/directory/xpcom/base/public/nsILDAPURL.idl
@@ -47,18 +47,43 @@
 /**
  * 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(7310562d-1567-43c7-a123-633c1ba3663e)]
+[scriptable, uuid(1018ee06-f708-4bd7-b7df-11285332a404)]
 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
@@ -146,10 +171,9 @@ interface nsILDAPURL : nsIURI {
      * @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;
-
 };
--- a/directory/xpcom/base/src/Makefile.in
+++ b/directory/xpcom/base/src/Makefile.in
@@ -51,30 +51,32 @@ IS_COMPONENT	= 1
 MODULE_NAME	= nsLDAPProtocolModule
 MOZILLA_INTERNAL_API = 1
 
 REQUIRES	= xpcom \
 		  string \
 		  necko \
 		  $(NULL)
 
+EXTRA_PP_COMPONENTS = \
+		nsLDAPProtocolHandler.js \
+		$(NULL)
 
 CPPSRCS		= \
 		nsLDAPProtocolModule.cpp \
 		nsLDAPMessage.cpp \
 		nsLDAPConnection.cpp \
 		nsLDAPOperation.cpp \
 		nsLDAPURL.cpp \
 		nsLDAPServer.cpp \
 		nsLDAPService.cpp \
 		nsLDAPBERValue.cpp \
 		nsLDAPControl.cpp \
 		nsLDAPBERElement.cpp \
 		nsLDAPModification.cpp \
-		nsLDAPProtocolHandler.cpp \
 		$(NULL)
 
 ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
 DEFINES		+= -DMOZ_LDAP_XPCOM_EXPERIMENTAL
 
 CPPSRCS		+= \
 		nsLDAPChannel.cpp \
 		$(NULL)
--- a/directory/xpcom/base/src/nsLDAPChannel.h
+++ b/directory/xpcom/base/src/nsLDAPChannel.h
@@ -57,16 +57,21 @@
 #include "nsILDAPURL.h"
 
 // if the code related to the following #define ever gets removed, also
 // be sure to remove mCallback as well as the most (but not all) of the 
 // various mUnproxied stuff
 //
 #define INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD 0
 
+// {97cce72b-8ce9-466e-b21d-05da7c1e02a6}
+#define NS_LDAPCHANNEL_CID \
+  { 0x97cce72b, 0x8ce9, 0x466e, \
+      { 0xb2, 0x1d, 0x05, 0xda, 0x7c, 0x1e, 0x02, 0xa6 } }
+
 class nsLDAPChannel : public nsIChannel, public nsILDAPMessageListener
 {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUEST
     NS_DECL_NSICHANNEL
     NS_DECL_NSILDAPMESSAGELISTENER
 
deleted file mode 100644
--- a/directory/xpcom/base/src/nsLDAPProtocolHandler.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * 
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the mozilla.org LDAP XPCOM SDK.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dan Mosedale <dmose@mozilla.org>
- *   Brian Ryner <bryner@brianryner.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsLDAPProtocolHandler.h"
-#include "nsCRT.h"
-#include "nsIComponentManager.h"
-#include "nsLDAPURL.h"
-#include "nsLDAPChannel.h"
-
-static NS_DEFINE_CID(kLDAPURLCID, NS_LDAPURL_CID);
-
-// QueryInterface, AddRef, and Release
-//
-NS_IMPL_ISUPPORTS1(nsLDAPProtocolHandler, nsIProtocolHandler)
-
-nsLDAPProtocolHandler::nsLDAPProtocolHandler()
-{
-}
-
-nsLDAPProtocolHandler::~nsLDAPProtocolHandler()
-{
-}
-
-// nsIProtocolHandler methods
-
-// getter method for scheme attr
-//
-NS_IMETHODIMP
-nsLDAPProtocolHandler::GetScheme(nsACString &result)
-{
-  result = "ldap";
-  return NS_OK;
-}
-
-// getter method for defaultPort attribute
-//
-NS_IMETHODIMP
-nsLDAPProtocolHandler::GetDefaultPort(PRInt32 *result)
-{
-  *result = 389;
-  return NS_OK;
-}
-
-// getter method for protocol flags attribute
-//
-NS_IMETHODIMP
-nsLDAPProtocolHandler::GetProtocolFlags(PRUint32 *result)
-{
-  *result = URI_NORELATIVE | URI_DANGEROUS_TO_LOAD;
-  return NS_OK;
-}
-
-// construct an appropriate URI
-//
-NS_IMETHODIMP
-nsLDAPProtocolHandler::NewURI(const nsACString &aSpec,
-                              const char *aOriginCharset, // ignored
-                              nsIURI *aBaseURI,
-                              nsIURI **result) 
-{
-    nsCOMPtr<nsILDAPURL> url;
-    nsresult rv;
-
-    url = do_CreateInstance(kLDAPURLCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // XXX - better error handling
-    //
-    rv = url->SetSpec(aSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // this is a getter, so we need to AddRef on the way out
-    //
-    *result = url;
-    NS_ADDREF(*result);
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsLDAPProtocolHandler::NewChannel(nsIURI* uri, 
-                                  nsIChannel* *result)
-{
-#ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
-    NS_ENSURE_ARG_POINTER(uri);
-    nsresult rv;
-    nsLDAPChannel *channel;
-
-    rv = nsLDAPChannel::Create(0, NS_GET_IID(nsIChannel),
-                               reinterpret_cast<void **>(&channel));
-    NS_ENSURE_SUCCESS(rv, rv);
-  
-    rv = channel->Init(uri);
-    if (NS_FAILED(rv)) {
-        NS_RELEASE(channel);
-        return rv;
-    }
-    // the channel was already AddRefed for us, and since this function itself
-    // is a getter, there's no need to release it here.
-    //
-    *result = channel;
-
-    return NS_OK;
-#else
-    return NS_ERROR_NOT_IMPLEMENTED;
-#endif
-}
-
-NS_IMETHODIMP 
-nsLDAPProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
-{
-    if (port == 389 || port == 636)  // 636 is LDAP/SSL
-        *_retval = PR_TRUE;
-    else
-        *_retval = PR_FALSE;
-    return NS_OK;
-}
-
deleted file mode 100644
--- a/directory/xpcom/base/src/nsLDAPProtocolHandler.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * 
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the mozilla.org LDAP XPCOM SDK.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dan Mosedale <dmose@mozilla.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nsLDAPProtocolHandler_h__
-#define nsLDAPProtocolHandler_h__
-
-#include "nsIProtocolHandler.h"
-
-// 9817dcda-8a67-4c30-b96f-ec4e647b0043 
-#define NS_LDAPPROTOCOLHANDLER_CID \
-{ 0x9817dcda, 0x8a67, 0x4c30, \
-  {0xb9, 0x6f, 0xec, 0x4e, 0x64, 0x7b, 0x00, 0x43} \
-} 
-
-class nsLDAPProtocolHandler : public nsIProtocolHandler
-{
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIPROTOCOLHANDLER
-
-    nsLDAPProtocolHandler();
-    virtual ~nsLDAPProtocolHandler();
-};
-
-#endif // nsLDAPProtocolHandler_h__
new file mode 100644
--- /dev/null
+++ b/directory/xpcom/base/src/nsLDAPProtocolHandler.js
@@ -0,0 +1,100 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is LDAP Protocol Handler.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Messaging.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mark Banner <bugzilla@standard8.plus.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const kNetworkProtocolCIDPrefix = "@mozilla.org/network/protocol;1?name=";
+const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
+
+function makeProtocolHandler(aProtocol, aDefaultPort) {
+  return {
+    classDescription: "LDAP Protocol Handler",
+    contractID: kNetworkProtocolCIDPrefix + aProtocol,
+    classID: Components.ID("{b3de9249-b0e5-4c12-8d91-c9a434fd80f5}"),
+    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)
+#ifdef USE_TK_LOGIN_MANAGER
+	url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD,
+		 aDefaultPort, aSpec, aOriginCharset, aBaseURI);
+#else
+	url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD,
+		 -1, aSpec, aOriginCharset, aBaseURI);
+#endif
+
+      return url;
+    },
+
+    newChannel: function (aURI) {
+      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("ldap", 389);
+
+function nsLDAPSProtocolHandler() {}
+
+nsLDAPSProtocolHandler.prototype = makeProtocolHandler("ldaps", 636);
+
+function NSGetModule(compMgr, fileSpec) {
+  return XPCOMUtils.generateModule([nsLDAPProtocolHandler,
+                                    nsLDAPSProtocolHandler]);
+}
--- a/directory/xpcom/base/src/nsLDAPProtocolModule.cpp
+++ b/directory/xpcom/base/src/nsLDAPProtocolModule.cpp
@@ -47,37 +47,38 @@
 #include "nsLDAPOperation.h"
 #include "nsLDAPMessage.h"
 #include "nsLDAPModification.h"
 #include "nsLDAPServer.h"
 #include "nsLDAPService.h"
 #include "nsLDAPBERValue.h"
 #include "nsLDAPBERElement.h"
 #include "nsLDAPControl.h"
-#include "nsLDAPProtocolHandler.h"
 
 #include "ldappr.h"
 
 #ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
 #include "nsLDAPChannel.h"
 #endif
 
 // 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_INIT(nsLDAPURL, Init)
+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)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPProtocolHandler)
+#ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPChannel)
+#endif
 
 NS_DECL_CLASSINFO(nsLDAPMessage)
 NS_DECL_CLASSINFO(nsLDAPOperation)
 NS_DECL_CLASSINFO(nsLDAPConnection)
 
 // a table of the CIDs implemented by this module
 //
 static const nsModuleComponentInfo components[] =
@@ -110,25 +111,26 @@ static const nsModuleComponentInfo compo
       &NS_CLASSINFO_NAME(nsLDAPMessage),
       nsIClassInfo::THREADSAFE },
     { "LDAP Modification", NS_LDAPMODIFICATION_CID,
           "@mozilla.org/network/ldap-modification;1", nsLDAPModificationConstructor },
     { "LDAP Server", NS_LDAPSERVER_CID,
           "@mozilla.org/network/ldap-server;1", nsLDAPServerConstructor },
     { "LDAP Service", NS_LDAPSERVICE_CID,
           "@mozilla.org/network/ldap-service;1", nsLDAPServiceConstructor },
-    { "LDAP Protocol Handler", NS_LDAPPROTOCOLHANDLER_CID, 
-          NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ldap", 
-          nsLDAPProtocolHandlerConstructor },   
     { "LDAP URL", NS_LDAPURL_CID,
           "@mozilla.org/network/ldap-url;1", nsLDAPURLConstructor },
     { "LDAP BER Value", NS_LDAPBERVALUE_CID,
       "@mozilla.org/network/ldap-ber-value;1", nsLDAPBERValueConstructor },
     { "LDAP BER Element", NS_LDAPBERELEMENT_CID,
       "@mozilla.org/network/ldap-ber-element;1", nsLDAPBERElementConstructor },
+#ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
+    { "LDAP Channel", NS_LDAPCHANNEL_CID,
+      "@mozilla.org/network/ldap-channel;1", nsLDAPChannelConstructor },
+#endif
     { "LDAP Control", NS_LDAPCONTROL_CID,
       "@mozilla.org/network/ldap-control;1", nsLDAPControlConstructor}
 };
 
 static nsresult
 nsLDAPInitialize(nsIModule *aSelf)
 {
 #ifdef PR_LOGGING
--- a/directory/xpcom/base/src/nsLDAPURL.cpp
+++ b/directory/xpcom/base/src/nsLDAPURL.cpp
@@ -38,339 +38,367 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsLDAPURL.h"
 #include "nsReadableUtils.h"
 #include "netCore.h"
 #include "plstr.h"
 #include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIStandardURL.h"
 
 // The two schemes we support, LDAP and LDAPS
 //
-static const char kLDAPScheme[] = "ldap";
-static const char kLDAPSSLScheme[] = "ldaps";
+NS_NAMED_LITERAL_CSTRING(LDAP_SCHEME, "ldap");
+NS_NAMED_LITERAL_CSTRING(LDAP_SSL_SCHEME, "ldaps");
 
-// Constructor and destructor
-//
 NS_IMPL_THREADSAFE_ISUPPORTS2(nsLDAPURL, nsILDAPURL, nsIURI)
 
 nsLDAPURL::nsLDAPURL()
-    : mPort(0),
-      mScope(SCOPE_BASE),
-      mOptions(0),
-      mAttributes(0)
+    : mScope(SCOPE_BASE),
+      mOptions(0)
 {
 }
 
 nsLDAPURL::~nsLDAPURL()
 {
-    // Delete the array of attributes
-    delete mAttributes;
 }
 
 nsresult
-nsLDAPURL::Init()
+nsLDAPURL::Init(PRUint32 aUrlType, PRInt32 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)
 {
-    if (!mAttributes) {
-        mAttributes = new nsCStringArray();
-        if (!mAttributes) {
-            NS_ERROR("nsLDAPURL::Init: out of memory ");
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
+  aPath.Assign('/');
+
+  if (!mDN.IsEmpty())
+    aPath.Append(mDN);
+
+  PRUint32 count = mAttributes.Count();
+  if (count)
+  {
+    aPath.Append('?');
+    PRUint32 index = 0;
+
+    while (index < count)
+    {
+      aPath.Append(*(mAttributes.CStringAt(index++)));
+      if (index < count)
+        aPath.Append(',');
+    }
+  }
+
+  if (mScope || !mFilter.IsEmpty())
+  {
+    aPath.Append((count ? "?" : "??"));
+    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)
+{
+  PRUint32 rv, count;
+  LDAPURLDesc *desc;
+  nsCString str;
+  char **attributes;
+
+  // This is from the LDAP C-SDK, which currently doesn't
+  // support everything from RFC 2255... :(
+  //
+  rv = ldap_url_parse(aPath.get(), &desc);
+  switch (rv) {
+  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;
+
+    // Set the attributes array, need to count it first.
+    //
+    count = 0;
+    attributes = desc->lud_attrs;
+    while (attributes && *attributes++)
+      count++;
+
+    if (count) {
+      rv = SetAttributes(count, const_cast<const char **>(desc->lud_attrs));
+      // This error could only be out-of-memory, so pass it up
+      //
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+    } else {
+      mAttributes.Clear();
     }
 
+    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.
-//
-// attribute string spec;
-//
+
 NS_IMETHODIMP 
 nsLDAPURL::GetSpec(nsACString &_retval)
 {
-    nsCAutoString spec;
-    PRUint32 count;
-    
-    spec = ((mOptions & OPT_SECURE) ? kLDAPSSLScheme : kLDAPScheme);
-    spec.Append("://");
-    if (!mHost.IsEmpty()) {
-        spec.Append(mHost);
-    }
-    if (mPort > 0) {
-        spec.Append(':');
-        spec.AppendInt(mPort);
-    }
-    spec.Append('/');
-    if (!mDN.IsEmpty()) {
-        spec.Append(mDN);
-    }
-
-    if ((count = mAttributes->Count())) {
-        PRUint32 index = 0;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-        spec.Append('?');
-        while (index < count) {
-            spec.Append(*(mAttributes->CStringAt(index++)));
-            if (index < count) {
-                spec.Append(',');
-            }
-        }
-    }
+  return mBaseURL->GetSpec(_retval);
+}
 
-    if (mScope || !mFilter.IsEmpty()) {
-        spec.Append((count ? "?" : "??"));
-        if (mScope) {
-            if (mScope == SCOPE_ONELEVEL) {
-                spec.Append("one");
-            } else if (mScope == SCOPE_SUBTREE) {
-                spec.Append("sub");
-            }
-        }
-        if (!mFilter.IsEmpty()) {
-            spec.Append('?');
-            spec.Append(mFilter);
-        }
-    }
-
-    _retval = spec;
-    return NS_OK;
-}
 NS_IMETHODIMP 
 nsLDAPURL::SetSpec(const nsACString &aSpec)
 {
-    PRUint32 rv, count;
-    LDAPURLDesc *desc;
-    nsCString str;
-    char **attributes;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-    // This is from the LDAP C-SDK, which currently doesn't
-    // support everything from RFC 2255... :(
-    //
-    rv = ldap_url_parse(PromiseFlatCString(aSpec).get(), &desc);
-    switch (rv) {
-    case LDAP_SUCCESS:
-        mHost = desc->lud_host;
-        mPort = desc->lud_port;
-        mDN = desc->lud_dn;
-        mScope = desc->lud_scope;
-        mFilter = desc->lud_filter;
-        mOptions = desc->lud_options;
+  // 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);
 
-        // Set the attributes array, need to count it first.
-        //
-        count = 0;
-        attributes = desc->lud_attrs;
-        while (attributes && *attributes++) {
-            count++;
-        }
-        if (count) {
-            rv = SetAttributes(count,
-                               const_cast<const char **>(desc->lud_attrs));
-            // This error could only be out-of-memory, so pass it up
-            //
-            if (NS_FAILED(rv)) {
-                return rv;
-            }
-        } else {
-            mAttributes->Clear();
-        }
+  rv = mBaseURL->SetSpec(aSpec);
+  NS_ENSURE_SUCCESS(rv, 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;
+  rv = SetPathInternal(nsPromiseFlatCString(aSpec));
+  if (NS_FAILED(rv))
+    mBaseURL->SetSpec(originalSpec);
 
-    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;
+  return rv;
 }
 
-// attribute string prePath;
-//
 NS_IMETHODIMP nsLDAPURL::GetPrePath(nsACString &_retval)
 {
-    _retval.Truncate();
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->GetPrePath(_retval);
 }
 
-// attribute string scheme;
-//
 NS_IMETHODIMP nsLDAPURL::GetScheme(nsACString &_retval)
 {
-    _retval = (mOptions & OPT_SECURE) ? kLDAPSSLScheme : kLDAPScheme;
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->GetScheme(_retval);
 }
+
 NS_IMETHODIMP nsLDAPURL::SetScheme(const nsACString &aScheme)
 {
-    if (aScheme.Equals(kLDAPScheme, nsCaseInsensitiveCStringComparator())) {
-        mOptions ^= OPT_SECURE;
-    } else if (aScheme.Equals(kLDAPSSLScheme, nsCaseInsensitiveCStringComparator())) {
-        mOptions |= OPT_SECURE;
-    } else {
-        return NS_ERROR_MALFORMED_URI;
-    }
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-    return NS_OK;
+  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);
 }
 
-// attribute string userPass;
-//
 NS_IMETHODIMP 
 nsLDAPURL::GetUserPass(nsACString &_retval)
 {
-    _retval.Truncate();
-    return NS_OK;
-}
-NS_IMETHODIMP
-nsLDAPURL::SetUserPass(const nsACString &aPreHost)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
+  _retval.Truncate();
+  return NS_OK;
 }
 
-// attribute string username
-//
+NS_IMETHODIMP
+nsLDAPURL::SetUserPass(const nsACString &aUserPass)
+{
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsLDAPURL::GetUsername(nsACString &_retval)
 {
-    _retval.Truncate();
-    return NS_OK;
+  _retval.Truncate();
+  return NS_OK;
 }
+
 NS_IMETHODIMP
 nsLDAPURL::SetUsername(const nsACString &aUsername)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  return NS_OK;
 }
 
-// attribute string password;
-//
 NS_IMETHODIMP 
 nsLDAPURL::GetPassword(nsACString &_retval)
 {
-    _retval.Truncate();
-    return NS_OK;
+  _retval.Truncate();
+  return NS_OK;
 }
+
 NS_IMETHODIMP 
 nsLDAPURL::SetPassword(const nsACString &aPassword)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  return NS_OK;
 }
 
-// attribute string hostPort;
-//
 NS_IMETHODIMP 
 nsLDAPURL::GetHostPort(nsACString &_retval)
 {
-  nsCString result(mHost);
-  result.AppendLiteral(":");
-  if (mPort)
-    result.AppendInt(mPort);
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-  _retval.Assign(result);
+  return mBaseURL->GetHostPort(_retval);
+}
 
-  return NS_OK;
-}
 NS_IMETHODIMP 
 nsLDAPURL::SetHostPort(const nsACString &aHostPort)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->SetHostPort(aHostPort);
 }
 
-// attribute string host;
-//
 NS_IMETHODIMP 
 nsLDAPURL::GetHost(nsACString &_retval)
 {
-    _retval = mHost;
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->GetHost(_retval);
 }
+
 NS_IMETHODIMP 
 nsLDAPURL::SetHost(const nsACString &aHost)
 {
-    mHost = aHost;
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->SetHost(aHost);
 }
 
-// C-SDK URL parser defaults port 389 as "0", while nsIURI
-// specifies the default to be "-1", hence the translations.
-//
-// attribute long port;
-//
 NS_IMETHODIMP 
 nsLDAPURL::GetPort(PRInt32 *_retval)
 {
-    if (!_retval) {
-        NS_ERROR("nsLDAPURL::GetPort: null pointer ");
-        return NS_ERROR_NULL_POINTER;
-    }
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-    if (!mPort) {
-        *_retval = -1;
-    } else {
-        *_retval = mPort;
-    }
-        
-    return NS_OK;
+  return mBaseURL->GetPort(_retval);
 }
+
 NS_IMETHODIMP 
 nsLDAPURL::SetPort(PRInt32 aPort)
 {
-    if (aPort == -1) {
-        mPort = 0;
-    } else if (aPort >= 0) {
-        mPort = aPort;
-    } else {
-        return NS_ERROR_MALFORMED_URI;
-    }
-    
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->SetPort(aPort);
 }
 
-// attribute string path;
-// XXXleif: For now, these are identical to SetDn()/GetDn().
 NS_IMETHODIMP nsLDAPURL::GetPath(nsACString &_retval)
 {
-    _retval = mDN;
-    return NS_OK;
-}
-NS_IMETHODIMP nsLDAPURL::SetPath(const nsACString &aPath)
-{
-    mDN = aPath;
-    return NS_OK;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->GetPath(_retval);
 }
 
-// attribute string specA
+NS_IMETHODIMP nsLDAPURL::SetPath(const nsACString &aPath)
+{
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  nsresult rv = SetPathInternal(nsPromiseFlatCString(aPath));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return mBaseURL->SetPath(aPath);
+}
+
 NS_IMETHODIMP nsLDAPURL::GetAsciiSpec(nsACString &_retval)
 {
-    return GetSpec(_retval);
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  // XXX handle extra items?
+  return mBaseURL->GetAsciiSpec(_retval);
 }
-// attribute string hostA
+
 NS_IMETHODIMP nsLDAPURL::GetAsciiHost(nsACString &_retval)
 {
-    return GetHost(_retval);
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  return mBaseURL->GetAsciiHost(_retval);
 }
 
 NS_IMETHODIMP nsLDAPURL::GetOriginCharset(nsACString &result)
 {
-    result.Truncate();
-    return NS_OK;
+  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, PRBool *_retval)
 {
   *_retval = PR_FALSE;
   if (other)
@@ -395,211 +423,261 @@ NS_IMETHODIMP nsLDAPURL::Equals(nsIURI *
         *_retval = PR_TRUE;
     }
   }
   return NS_OK;
 }
 
 // boolean schemeIs(in const char * scheme);
 //
-NS_IMETHODIMP nsLDAPURL::SchemeIs(const char *i_Scheme, PRBool *o_Equals)
+NS_IMETHODIMP nsLDAPURL::SchemeIs(const char *aScheme, PRBool *aEquals)
 {
-    if (!i_Scheme) return NS_ERROR_INVALID_ARG;
-    if (*i_Scheme == 'l' || *i_Scheme == 'L') {
-        *o_Equals = PL_strcasecmp("ldap", i_Scheme) ? PR_FALSE : PR_TRUE;
-    } else {
-        *o_Equals = PR_FALSE;
-    }
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-    return NS_OK;
+  return mBaseURL->SchemeIs(aScheme, aEquals);
 }
 
 // nsIURI clone ();
 //
-NS_IMETHODIMP nsLDAPURL::Clone(nsIURI **_retval)
+NS_IMETHODIMP nsLDAPURL::Clone(nsIURI **aResult)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_ARG_POINTER(aResult);
+
+  nsLDAPURL *clone;
+  NS_NEWXPCOM(clone, 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 = mBaseURL->Clone(getter_AddRefs(clone->mBaseURL));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ADDREF(*aResult = clone);
+  return NS_OK;
 }
 
 // string resolve (in string relativePath);
 //
 NS_IMETHODIMP nsLDAPURL::Resolve(const nsACString &relativePath,
                                  nsACString &_retval)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  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)
 {
-    mDN.Assign(aDn);
-    return NS_OK;
+  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);
 }
 
 // void getAttributes (out unsigned long aCount, 
 //                     [array, size_is (aCount), retval] out string aAttrs);
 //
 NS_IMETHODIMP nsLDAPURL::GetAttributes(PRUint32 *aCount, char ***_retval)
 {
+    NS_ENSURE_ARG_POINTER(aCount);
+    NS_ENSURE_ARG_POINTER(_retval);
+
     PRUint32 index = 0;
     PRUint32 count;
     char **cArray = nsnull;
 
     if (!_retval) {
         NS_ERROR("nsLDAPURL::GetAttributes: null pointer ");
         return NS_ERROR_NULL_POINTER;
     }
 
-    count = mAttributes->Count();
+    count = mAttributes.Count();
     if (count > 0) {
         cArray = static_cast<char **>(nsMemory::Alloc(count * sizeof(char *)));
         if (!cArray) {
             NS_ERROR("nsLDAPURL::GetAttributes: out of memory ");
             return NS_ERROR_OUT_OF_MEMORY;
         }
 
         // Loop through the string array, and build up the C-array.
         //
         while (index < count) {
-            if (!(cArray[index] = ToNewCString(*(mAttributes->CStringAt(index))))) {
+            if (!(cArray[index] = ToNewCString(*(mAttributes.CStringAt(index))))) {
                 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, cArray);
                 NS_ERROR("nsLDAPURL::GetAttributes: out of memory ");
                 return NS_ERROR_OUT_OF_MEMORY;
             }
             index++;
         }
     }
     *aCount = count;
     *_retval = cArray;
 
     return NS_OK;
 }
 // void setAttributes (in unsigned long aCount,
 //                     [array, size_is (aCount)] in string aAttrs); */
 NS_IMETHODIMP nsLDAPURL::SetAttributes(PRUint32 count, const char **aAttrs)
 {
-    PRUint32 index = 0;
-    nsCString str;
-    
-    mAttributes->Clear();
-    while (index < count) {
-        // Have to assign the str into this temporary nsCString, to make
-        // the compilers happy...
-        //
-        str = nsDependentCString(aAttrs[index]);
-        if (!mAttributes->InsertCStringAt(str, index++)) {
-            NS_ERROR("nsLDAPURL::SetAttributes: out of memory ");
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  if (count)
+    NS_ENSURE_ARG_POINTER(aAttrs);
+
+  mAttributes.Clear();
+  for (PRUint32 i = 0; i < count; ++i)
+  {
+    if (!mAttributes.AppendCString(nsDependentCString(aAttrs[i])))
+    {
+      NS_ERROR("nsLDAPURL::SetAttributes: out of memory ");
+      return NS_ERROR_OUT_OF_MEMORY;
     }
+  }
 
-    return NS_OK;
+  // Now get the current path
+  nsCString newPath;
+  GetPathInternal(newPath);
+
+  // and update the base url
+  return mBaseURL->SetPath(newPath);
 }
-// void addAttribute (in string aAttribute);
-//
+
 NS_IMETHODIMP nsLDAPURL::AddAttribute(const char *aAttribute)
 {
-    nsCString str;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  NS_ENSURE_ARG_POINTER(aAttribute);
 
-    str = nsDependentCString(aAttribute);
-    if (mAttributes->IndexOfIgnoreCase(str) >= 0) {
-        return NS_OK;
-    }
+  nsDependentCString str(aAttribute);
+
+  if (mAttributes.IndexOfIgnoreCase(str) >= 0)
+    return NS_OK;
 
-    if (!mAttributes->InsertCStringAt(str, mAttributes->Count())) {
-        NS_ERROR("nsLDAPURL::AddAttribute: out of memory ");
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
+  if (!mAttributes.AppendCString(str)) {
+    NS_ERROR("nsLDAPURL::AddAttribute: out of memory ");
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-    return NS_OK;
+  // Now get the current path
+  nsCString newPath;
+  GetPathInternal(newPath);
+
+  // and update the base url
+  return mBaseURL->SetPath(newPath);
 }
-// void removeAttribute (in string aAttribute);
-//
+
 NS_IMETHODIMP nsLDAPURL::RemoveAttribute(const char *aAttribute)
 {
-    nsCString str;
+  if (!mBaseURL)
+    return NS_ERROR_NOT_INITIALIZED;
 
-    str = nsDependentCString(aAttribute);
-    mAttributes->RemoveCString(str);
+  NS_ENSURE_ARG_POINTER(aAttribute);
+  mAttributes.RemoveCString(nsDependentCString(aAttribute));
 
-    return NS_OK;
+  // Now get the current path
+  nsCString newPath;
+  GetPathInternal(newPath);
+
+  // and update the base url
+  return mBaseURL->SetPath(newPath);
 }
-// boolean hasAttribute (in string aAttribute);
-//
+
 NS_IMETHODIMP nsLDAPURL::HasAttribute(const char *aAttribute, PRBool *_retval)
 {
-    nsCString str;
+  NS_ENSURE_ARG_POINTER(aAttribute);
+  NS_ENSURE_ARG_POINTER(_retval);
 
-    if (!_retval) {
-        NS_ERROR("nsLDAPURL::HasAttribute: null pointer ");
-        return NS_ERROR_NULL_POINTER;
-    }
-
-    str = nsDependentCString(aAttribute);
-    *_retval = mAttributes->IndexOfIgnoreCase(str) >= 0;
-    
-    return NS_OK;
+  *_retval = mAttributes.IndexOfIgnoreCase(nsDependentCString(aAttribute)) >= 0;
+  return NS_OK;
 }
 
-// attribute long scope;
-//
 NS_IMETHODIMP nsLDAPURL::GetScope(PRInt32 *_retval)
 {
-    if (!_retval) {
-        NS_ERROR("nsLDAPURL::GetScope: null pointer ");
-        return NS_ERROR_NULL_POINTER;
-    }
+  NS_ENSURE_ARG_POINTER(_retval);
+  *_retval = mScope;
+  return NS_OK;
+}
 
-    *_retval = mScope;
-    return NS_OK;
-}
 NS_IMETHODIMP nsLDAPURL::SetScope(PRInt32 aScope)
 {
-    // Only allow scopes supported by the C-SDK
-    if ((aScope != SCOPE_BASE) &&
-        (aScope != SCOPE_ONELEVEL) &&
-        (aScope != SCOPE_SUBTREE)) {
-        return NS_ERROR_MALFORMED_URI;
-    }
+  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;
+  mScope = aScope;
 
-    return NS_OK;
+  // Now get the current path
+  nsCString newPath;
+  GetPathInternal(newPath);
+
+  // and update the base url
+  return mBaseURL->SetPath(newPath);
 }
 
-// attribute string filter;
-//
 NS_IMETHODIMP nsLDAPURL::GetFilter(nsACString& _retval)
 {
     _retval.Assign(mFilter);
     return NS_OK;
 }
 NS_IMETHODIMP nsLDAPURL::SetFilter(const nsACString& aFilter)
 {
-    mFilter.Assign(aFilter);
-    return NS_OK;
+  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);
 }
 
-// attribute unsigned long options;
-//
 NS_IMETHODIMP nsLDAPURL::GetOptions(PRUint32 *_retval)
 {
-    if (!_retval) {
-        NS_ERROR("nsLDAPURL::GetOptions: null pointer ");
-        return NS_ERROR_NULL_POINTER;
-    }
+  NS_ENSURE_ARG_POINTER(_retval);
+  *_retval = mOptions;
+  return NS_OK;
+}
 
-    *_retval = mOptions;
-    return NS_OK;
-}
 NS_IMETHODIMP nsLDAPURL::SetOptions(PRUint32 aOptions)
 {
-    mOptions = 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);
 }
--- a/directory/xpcom/base/src/nsLDAPURL.h
+++ b/directory/xpcom/base/src/nsLDAPURL.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -37,34 +37,52 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ldap.h"
 #include "nsString.h"
 #include "nsVoidArray.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_ISUPPORTS
-    NS_DECL_NSIURI
-    NS_DECL_NSILDAPURL
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIURI
+  NS_DECL_NSILDAPURL
 
-    nsLDAPURL();
-    virtual ~nsLDAPURL();
-    nsresult Init();
+  nsLDAPURL();
+  virtual ~nsLDAPURL();
+
+protected:
 
-  protected:
-    nsCString mHost;              // Host name of this Directory server
-    PRInt32 mPort;                // LDAP port number
-    nsCString mDN;                // Base Distinguished Name (Base DN)
-    PRInt32 mScope;               // Search scope (base, one or sub)
-    nsCString mFilter;            // LDAP search filter
-    PRUint32 mOptions;            // Options
-    nsCStringArray *mAttributes;  // List of attributes
+  void GetPathInternal(nsCString &aPath);
+  nsresult SetPathInternal(const nsCString &aPath);
+
+  nsCString mDN;                // Base Distinguished Name (Base DN)
+  PRInt32 mScope;               // Search scope (base, one or sub)
+  nsCString mFilter;            // LDAP search filter
+  PRUint32 mOptions;            // Options
+  nsCStringArray mAttributes;  // List of attributes
+  nsCOMPtr<nsIURI> mBaseURL;
 };
--- a/directory/xpcom/tests/Makefile.in
+++ b/directory/xpcom/tests/Makefile.in
@@ -38,27 +38,37 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE		= rdfldapds
+# Module name for xpcshell tests.
+MODULE		= test_ldapxpcom
+
+XPCSHELL_TESTS	= unit
 
 include $(topsrcdir)/config/rules.mk
 
+# Note that for the time being "ldapshell" requires LDAP Experimental
+#
+
+ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
+
+DEFINES += -DMOZ_LDAP_XPCOM_EXPERIMENTAL
 
 ifeq ($(OS_ARCH),WINNT)
 # need this because xpcshell built with readline won't work in cygwin shells
 RUN		= cmd /c start
 else
 RUN		= $(DIST)/bin/run-mozilla.sh
 endif
 
 XPCSHELL	= $(DIST)/bin/xpcshell
 
 .PHONY: ldapshell
 ldapshell:
 	$(CYGWIN_WRAPPER) $(RUN) $(XPCSHELL) \
 		-f $(srcdir)/ldapshell.js \
 		-f -
+endif
--- a/directory/xpcom/tests/jar.mn
+++ b/directory/xpcom/tests/jar.mn
@@ -1,3 +1,5 @@
+#ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL
 comm.jar:
     content/communicator/ldapviewer/example.xul
     content/communicator/ldapviewer/example.rdf
+#endif
new file mode 100644
--- /dev/null
+++ b/directory/xpcom/tests/unit/test_nsLDAPURL.js
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Test suite for nsLDAPURL functions.
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+// If we are still using the wallet service, then default port numbers
+// are still visible in the password manager, and therefore we need to have
+// them in the url. The toolkit login manager doesn't do this.
+const usingWallet = "nsIWalletService" in Ci;
+const portAdpt = usingWallet ? ":389" : "";
+
+const ldapURLs =
+  [ { url: "ldap://localhost/dc=test",
+      spec: "ldap://localhost/dc=test",
+      asciiSpec: "ldap://localhost/dc=test",
+      host: "localhost",
+      asciiHost: "localhost",
+      port: -1,
+      scheme: "ldap",
+      path: "/dc=test",
+      prePath: "ldap://localhost",
+      hostPort: "localhost",
+      dn: "dc=test",
+      scope: Ci.nsILDAPURL.SCOPE_BASE,
+      filter: "(objectclass=*)",
+      options: 0
+    },
+    { url: "ldap://localhost:389/dc=test,dc=abc??sub?(objectclass=*)",
+      spec: "ldap://localhost" + portAdpt + "/dc=test,dc=abc??sub?(objectclass=*)",
+      asciiSpec: "ldap://localhost" + portAdpt + "/dc=test,dc=abc??sub?(objectclass=*)",
+      host: "localhost",
+      asciiHost: "localhost",
+      port: usingWallet ? 389 : -1,
+      scheme: "ldap",
+      path: "/dc=test,dc=abc??sub?(objectclass=*)",
+      prePath: "ldap://localhost" + portAdpt,
+      hostPort: "localhost" + portAdpt,
+      dn: "dc=test,dc=abc",
+      scope: Ci.nsILDAPURL.SCOPE_SUBTREE,
+      filter: "(objectclass=*)",
+      options: 0
+    },
+    { url: "ldap://\u65e5\u672c\u8a93.jp:389/dc=tes\u65e5t??one?(oc=xyz)",
+      spec: "ldap://\u65e5\u672c\u8a93.jp" + portAdpt + "/dc=tes%E6%97%A5t??one?(oc=xyz)",
+      asciiSpec: "ldap://xn--wgv71a309e.jp" + portAdpt + "/dc=tes%E6%97%A5t??one?(oc=xyz)",
+      host: "\u65e5\u672c\u8a93.jp",
+      asciiHost: "xn--wgv71a309e.jp",
+      port: usingWallet ? 389 : -1,
+      scheme: "ldap",
+      path: "/dc=tes%E6%97%A5t??one?(oc=xyz)",
+      prePath: "ldap://\u65e5\u672c\u8a93.jp" + portAdpt,
+      hostPort: "\u65e5\u672c\u8a93.jp" + portAdpt,
+      dn: "dc=tes\u65e5t",
+      scope: Ci.nsILDAPURL.SCOPE_ONELEVEL,
+      filter: "(oc=xyz)",
+      options: 0
+    },
+    { url: "ldaps://localhost/dc=test",
+      spec: "ldaps://localhost/dc=test",
+      asciiSpec: "ldaps://localhost/dc=test",
+      host: "localhost",
+      asciiHost: "localhost",
+      port: -1,
+      scheme: "ldaps",
+      path: "/dc=test",
+      prePath: "ldaps://localhost",
+      hostPort: "localhost",
+      dn: "dc=test",
+      scope: Ci.nsILDAPURL.SCOPE_BASE,
+      filter: "(objectclass=*)",
+      options: Ci.nsILDAPURL.OPT_SECURE
+    }
+  ];
+
+function run_test() {
+  var url;
+
+  // Get the IO service;
+  var ioService = Cc["@mozilla.org/network/io-service;1"]
+                    .getService(Ci.nsIIOService);
+
+  // Test - get and check urls.
+
+  var part = 0;
+  for (part = 0; part < ldapURLs.length; ++part)
+  {
+    dump("url: " + ldapURLs[part].url + "\n");
+    url = ioService.newURI(ldapURLs[part].url, null, null);
+
+    do_check_eq(url.spec, ldapURLs[part].spec);
+    do_check_eq(url.asciiSpec, ldapURLs[part].asciiSpec);
+    do_check_eq(url.scheme, ldapURLs[part].scheme);
+    do_check_eq(url.host, ldapURLs[part].host);
+    do_check_eq(url.asciiHost, ldapURLs[part].asciiHost);
+    do_check_eq(url.port, ldapURLs[part].port);
+    do_check_eq(url.path, ldapURLs[part].path);
+    do_check_eq(url.prePath, ldapURLs[part].prePath);
+    do_check_eq(url.hostPort, ldapURLs[part].hostPort);
+    // XXX nsLDAPURL ought to have classinfo.
+    url = url.QueryInterface(Ci.nsILDAPURL);
+    do_check_eq(url.dn, ldapURLs[part].dn);
+    do_check_eq(url.scope, ldapURLs[part].scope);
+    do_check_eq(url.filter, ldapURLs[part].filter);
+    do_check_eq(url.options, ldapURLs[part].options);
+  }
+
+  // Test - Check changing ldap values
+  dump("Other Tests\n");
+
+  // Start off with a base url
+  const kBaseURL = "ldap://localhost:389/dc=test,dc=abc??sub?(objectclass=*)";
+
+  url = ioService.newURI(kBaseURL, null, null)
+                 .QueryInterface(Ci.nsILDAPURL);
+
+  // Test - dn
+
+  url.dn = "dc=short";
+
+  do_check_eq(url.dn, "dc=short");
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??sub?(objectclass=*)");
+
+  // Test - scope
+
+  url.scope = Ci.nsILDAPURL.SCOPE_BASE;
+
+  do_check_eq(url.scope, Ci.nsILDAPURL.SCOPE_BASE);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short???(objectclass=*)");
+
+  url.scope = Ci.nsILDAPURL.SCOPE_ONELEVEL;
+
+  do_check_eq(url.scope, Ci.nsILDAPURL.SCOPE_ONELEVEL);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  // Test - filter
+
+  url.filter = "(oc=ygh)";
+
+  do_check_eq(url.filter, "(oc=ygh)");
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(oc=ygh)");
+
+  url.filter = "";
+
+  do_check_eq(url.filter, "(objectclass=*)");
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  // Test - scheme
+
+  // An old version used to have a bug whereby if you set the scheme to the
+  // same thing twice, you'd get the options set wrongly.
+  url.scheme = "ldaps";
+  do_check_eq(url.options, 1);
+  do_check_eq(url.spec, "ldaps://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+  url.scheme = "ldaps";
+  do_check_eq(url.options, 1);
+  do_check_eq(url.spec, "ldaps://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  do_check_true(url.schemeIs("ldaps"));
+  do_check_false(url.schemeIs("ldap"));
+
+  url.scheme = "ldap";
+  do_check_eq(url.options, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+  url.scheme = "ldap";
+  do_check_eq(url.options, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  do_check_true(url.schemeIs("ldap"));
+  do_check_false(url.schemeIs("ldaps"));
+
+  // Test - Options
+
+  url.options = Ci.nsILDAPURL.OPT_SECURE;
+
+  do_check_eq(url.options, Ci.nsILDAPURL.OPT_SECURE);
+  do_check_eq(url.spec, "ldaps://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  url.options = 0;
+
+  do_check_eq(url.options, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  // Test - Equals
+
+  var url2 = ioService.newURI("ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)", null, null)
+                      .QueryInterface(Ci.nsILDAPURL);
+
+  do_check_true(url.equals(url2));
+
+  url2.spec = "ldap://localhost:389/dc=short??sub?(objectclass=*)";
+
+  do_check_false(url.equals(url2));
+
+  // Test Attributes
+
+  var attrs = url.getAttributes({});
+
+  do_check_eq(attrs.length, 0);
+
+  // Nothing should happend if the attribute doesn't exist
+  url.removeAttribute("abc");
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  url.addAttribute("dn");
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short?dn?one?(objectclass=*)");
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, 1);
+  do_check_eq(attrs[0], "dn");
+
+  url.removeAttribute("dn");
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  var newAttrs = [ "abc", "def", "ghi", "jkl" ];
+
+  url.setAttributes(newAttrs.length, newAttrs);
+
+  var i;
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, newAttrs.length);
+  for (i = 0; i < newAttrs.length; ++i)
+    do_check_eq(attrs[i], newAttrs[i]);
+
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short?abc,def,ghi,jkl?one?(objectclass=*)");
+
+  do_check_true(url.hasAttribute("jkl"));
+  do_check_true(url.hasAttribute("def"));
+  do_check_true(url.hasAttribute("ABC"));
+  do_check_false(url.hasAttribute("cde"));
+  do_check_false(url.hasAttribute("3446"));
+
+  url.removeAttribute("abc");
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, newAttrs.length - 1);
+  for (i = 0; i < newAttrs.length - 1; ++i)
+    do_check_eq(attrs[i], newAttrs[i + 1]);
+
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short?def,ghi,jkl?one?(objectclass=*)");
+
+  // This shouldn't fail, just clear the list
+  url.setAttributes(0, []);
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, 0);
+  do_check_eq(url.spec, "ldap://localhost" + portAdpt + "/dc=short??one?(objectclass=*)");
+
+  // Set attributes via the url spec
+
+  url.spec = "ldap://localhost/dc=short?abc,def,ghi,jkl?one?(objectclass=*)";
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, newAttrs.length);
+  for (i = 0; i < newAttrs.length; ++i)
+    do_check_eq(attrs[i], newAttrs[i]);
+
+  do_check_eq(url.spec, "ldap://localhost/dc=short?abc,def,ghi,jkl?one?(objectclass=*)");
+
+  url.spec = "ldap://localhost/dc=short??one?(objectclass=*)";
+
+  attrs = url.getAttributes({});
+  do_check_eq(attrs.length, 0);
+  do_check_eq(url.spec, "ldap://localhost/dc=short??one?(objectclass=*)");
+
+  // Test - clone
+
+  url.spec = "ldap://localhost/dc=short?abc,def,ghi,jkl?one?(objectclass=*)";
+
+  var newUrl = url.clone();
+
+  do_check_eq(newUrl.spec,
+              "ldap://localhost/dc=short?abc,def,ghi,jkl?one?(objectclass=*)");
+}
--- a/mailnews/addrbook/prefs/resources/content/pref-directory-add.js
+++ b/mailnews/addrbook/prefs/resources/content/pref-directory-add.js
@@ -183,32 +183,38 @@ function fillSettings()
   document.getElementById("description").value = gCurrentDirectory.dirName;
 
   if (gCurrentDirectory instanceof Components.interfaces.nsIAbLDAPDirectory) {
     var ldapUrl = gCurrentDirectory.lDAPURL;
 
     document.getElementById("results").value = gCurrentDirectory.maxHits;
     document.getElementById("login").value = gCurrentDirectory.authDn;
     document.getElementById("hostname").value = ldapUrl.host;
-    document.getElementById("port").value = ldapUrl.port;
     document.getElementById("basedn").value = ldapUrl.dn;
     document.getElementById("search").value = ldapUrl.filter;
 
     var sub = document.getElementById("sub");
     switch(ldapUrl.scope) {
     case Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL:
       sub.radioGroup.selectedItem = document.getElementById("one");
       break;
     default:
       sub.radioGroup.selectedItem = sub;
       break;
     }
-    
-    if (ldapUrl.options & ldapUrl.OPT_SECURE)
+
+    var secure = ldapUrl.options & ldapUrl.OPT_SECURE
+    if (secure)
       document.getElementById("secure").setAttribute("checked", "true");
+
+    if (ldapUrl.port == -1)
+      document.getElementById("port").value =
+        (secure ? kDefaultSecureLDAPPort : kDefaultLDAPPort);
+    else
+      document.getElementById("port").value = ldapUrl.port;
   }
 
   // check if any of the preferences for this server are locked.
   //If they are locked disable them
   DisableUriFields(gCurrentDirectory.dirPrefId + ".uri");
   DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".description", "description");
   DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".disable_button_download", "download");
   DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".maxHits", "results");
@@ -280,59 +286,55 @@ function onAccept()
 {
   var addressbook = Components.classes["@mozilla.org/abmanager;1"]
                               .getService(Components.interfaces.nsIAbManager);
 
   try {
     var pref_string_content = "";
     var pref_string_title = "";
 
-    var ldapUrl = Components.classes["@mozilla.org/network/ldap-url;1"];
-    ldapUrl = ldapUrl.createInstance().
-      QueryInterface(Components.interfaces.nsILDAPURL);
-
     var description = document.getElementById("description").value;
     var hostname = document.getElementById("hostname").value;
     var port = document.getElementById("port").value;
     var secure = document.getElementById("secure");
     var results = document.getElementById("results").value;
     var errorValue = null;
     if ((!description) || hasOnlyWhitespaces(description))
       errorValue = "invalidName";
     else if ((!hostname) || hasOnlyWhitespaces(hostname))
       errorValue = "invalidHostname";
     // XXX write isValidDn and call it on the dn string here?
     else if (port && hasCharacters(port))
       errorValue = "invalidPortNumber";
     else if (results && hasCharacters(results))
       errorValue = "invalidResults";
     if (!errorValue) {
+      // XXX Due to the LDAP c-sdk pass a dummy url to the IO service, then
+      // update the parts (bug 473351).
+      var ldapUrl = Components.classes["@mozilla.org/network/io-service;1"]
+        .getService(Components.interfaces.nsIIOService)
+        .newURI((secure.checked ? "ldaps://" : "ldap://") + "localhost/dc=???",
+                null, null)
+        .QueryInterface(Components.interfaces.nsILDAPURL);
+
       ldapUrl.host = hostname;
+      ldapUrl.port = port ? port :
+                            (secure.checked ? kDefaultSecureLDAPPort :
+                                              kDefaultLDAPPort);
       ldapUrl.dn = document.getElementById("basedn").value;
+      ldapUrl.scope = document.getElementById("one").selected ?
+                      Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL :
+                      Components.interfaces.nsILDAPURL.SCOPE_SUBTREE;
+
       ldapUrl.filter = document.getElementById("search").value;
-      if (!port) {
-        if (secure.checked)
-          ldapUrl.port = kDefaultSecureLDAPPort;
-        else
-          ldapUrl.port = kDefaultLDAPPort;
-      } else {
-        ldapUrl.port = port;
-      }
-      if (document.getElementById("one").selected) {
-        ldapUrl.scope = Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL;
-      } else {
-        ldapUrl.scope = Components.interfaces.nsILDAPURL.SCOPE_SUBTREE;
-      }
-      if (secure.checked)
-        ldapUrl.options |= ldapUrl.OPT_SECURE;
 
       // check if we are modifying an existing directory or adding a new directory
       if (gCurrentDirectory) {
         gCurrentDirectory.dirName = description;
-        gCurrentDirectory.lDAPURL = ldapUrl;
+        gCurrentDirectory.lDAPURL = ldapUrl.QueryInterface(Components.interfaces.nsILDAPURL);
         window.opener.gNewServerString = gCurrentDirectory.dirPrefId;
       }
       else { // adding a new directory
         window.opener.gNewServerString =
           addressbook.newAddressBook(description, ldapUrl.spec, kLDAPDirectory);
       }
 
       // the rdf service
--- a/mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl
+++ b/mailnews/addrbook/public/nsIAbLDAPAttributeMap.idl
@@ -62,17 +62,17 @@ interface nsIAbCard;
  * property.  The checkState method is a useful tool in enforcing
  * this.  Failure to enforce it may make it impossible to guarantee
  * that getProperty will do something consistent and reasonable.
  *
  * Maybe someday once we support ldap autoconfig stuff (ie
  * draft-joslin-config-schema-11.txt), we can simplify this and other
  * code and only allow a property to map to a single attribute.
  */
-[scriptable, uuid(bc543118-db5d-4616-b2d4-9a8667609a7c)]
+[scriptable, uuid(dbfb9974-45ee-41c3-a005-2e8f002c1c9f)]
 interface nsIAbLDAPAttributeMap : nsISupports
 {
   /**
    * Get all the LDAP attributes associated with a given property
    * name, in order of precedence (highest to lowest).
    *
    * @param       aProperty   the address book property to return attrs for   
    *    
@@ -134,21 +134,22 @@ interface nsIAbLDAPAttributeMap : nsISup
    */
   ACString getProperty(in ACString aAttribute);
 
   /**
    * Get all attributes that may be used in an addressbook card via this
    * property map (used for passing to to an LDAP search when you want
    * everything that could be in a card returned).
    *
-   * @return                      a comma-separated list of attribute names
+   * @return                      an array of attribute names
    *
    * @exception NS_ERROR_FAILURE  there are no attributes in this property map
    */
-  ACString getAllCardAttributes();
+  void getAllCardAttributes(out unsigned long aCount,
+                            [retval, array, size_is(aCount)] out string aAttrs);
   
   /**
    * Get all properties that may be used in an addressbook card via this
    * property map.
    *
    * @return                      an array of properties
    *
    * @exception NS_ERROR_FAILURE  there are no attributes in this property map
--- a/mailnews/addrbook/src/Makefile.in
+++ b/mailnews/addrbook/src/Makefile.in
@@ -65,16 +65,17 @@ REQUIRES	= xpcom \
 		  uconv \
 		  msgbase \
 		  msgbaseutil \
 		  mime \
 		  intl \
 		  windowwatcher \
 		  uriloader \
 		  embed_base \
+		  loginmgr \
 		  $(NULL)
 
 CPPSRCS		= \
 		nsAbManager.cpp \
 		nsAbRDFDataSource.cpp \
 		nsDirectoryDataSource.cpp \
 		nsAbCardProperty.cpp \
 		nsDirPrefs.cpp \
--- a/mailnews/addrbook/src/nsAbLDAPAttributeMap.js
+++ b/mailnews/addrbook/src/nsAbLDAPAttributeMap.js
@@ -114,27 +114,28 @@ nsAbLDAPAttributeMap.prototype = {
 
     if (!(aAttribute in this.mAttrMap)) {
       return null;
     }
 
     return this.mAttrMap[aAttribute];
   },
 
-  getAllCardAttributes: function getAllCardAttributes() {
+  getAllCardAttributes: function getAllCardAttributes(aCount) {
     var attrs = [];
     for each (var prop in this.mPropertyMap) {
       attrs.push(prop);
     }
 
     if (!attrs.length) {
       throw Components.results.NS_ERROR_FAILURE;
     }
 
-    return attrs.join(",");
+    aCount.value = attrs.length;
+    return attrs;
   },
   
   getAllCardProperties: function getAllCardProperties(aCount) {
     
     var props = [];
     for (var prop in this.mPropertyMap) {
       props.push(prop);
     }
--- a/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
@@ -205,29 +205,25 @@ NS_IMETHODIMP nsAbLDAPDirectory::HasCard
 
   *hasCard = mCache.Get(card, nsnull);
   if (!*hasCard && mPerformingQuery)
     return NS_ERROR_NOT_AVAILABLE;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsAbLDAPDirectory::GetLDAPURL(nsILDAPURL** url)
+NS_IMETHODIMP nsAbLDAPDirectory::GetLDAPURL(nsILDAPURL** aResult)
 {
-  NS_ENSURE_ARG_POINTER(url);
-
-  nsresult rv;
-  nsCOMPtr<nsILDAPURL> result = do_CreateInstance(NS_LDAPURL_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_ARG_POINTER(aResult);
 
   // Rather than using GetURI here we call GetStringValue directly so
   // we can handle the case where the URI isn't specified (see comments
   // below)
   nsCAutoString URI;
-  rv = GetStringValue("uri", EmptyCString(), URI);
+  nsresult rv = GetStringValue("uri", EmptyCString(), URI);
   if (NS_FAILED(rv) || URI.IsEmpty())
   {
     /*
      * A recent change in Mozilla now means that the LDAP Address Book
      * RDF Resource URI is based on the unique preference name value i.e.  
      * [moz-abldapdirectory://prefName]
      * Prior to this valid change it was based on the actual uri i.e. 
      * [moz-abldapdirectory://host:port/basedn]
@@ -238,30 +234,29 @@ NS_IMETHODIMP nsAbLDAPDirectory::GetLDAP
      * products could integrate with Mozilla's LDAP Address Books
      * without necessarily having an entry in the preferences file
      * or more importantly needing to be able to change the
      * preferences entries. Thus to set the URI Spec now, it is
      * only necessary to read the uri pref entry, while in the
      * case where it is not a preference, we need to replace the
      * "moz-abldapdirectory".
      */
-    nsCAutoString tempLDAPURL(mURINoQuery);
-    if (StringBeginsWith(tempLDAPURL, NS_LITERAL_CSTRING(kLDAPDirectoryRoot)))
-      tempLDAPURL.Replace(0, kLDAPDirectoryRootLen, NS_LITERAL_CSTRING("ldap://"));
+    URI = mURINoQuery;
+    if (StringBeginsWith(URI, NS_LITERAL_CSTRING(kLDAPDirectoryRoot)))
+      URI.Replace(0, kLDAPDirectoryRootLen, NS_LITERAL_CSTRING("ldap://"));
+  }
 
-    rv = result->SetSpec(tempLDAPURL);
-  }
-  else
-  {
-    rv = result->SetSpec(URI);
-  }
+  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  result.swap(*url);
-  return rv;
+  nsCOMPtr<nsIURI> result;
+  rv = ioService->NewURI(URI, nsnull, nsnull, getter_AddRefs(result));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(result, aResult);
 }
 
 NS_IMETHODIMP nsAbLDAPDirectory::SetLDAPURL(nsILDAPURL *aUrl)
 {
   NS_ENSURE_ARG_POINTER(aUrl);
 
   nsCAutoString oldUrl;
   // Note, it doesn't matter if GetStringValue fails - we'll just send an
--- a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
@@ -421,42 +421,48 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
       if (login != mCurrentLogin || protocolVersion != mCurrentProtocolVersion)
       {
         redoConnection = PR_TRUE;
         mCurrentLogin = login;
         mCurrentProtocolVersion = protocolVersion;
       }
     }
   }
-  
-  // Now formulate the search string
-  
-  // Get the scope
-  nsCAutoString scope;
-  PRBool doSubDirectories;
-  rv = aArguments->GetQuerySubDirectories (&doSubDirectories);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = mDirectoryUrl->Clone(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
-  scope = (doSubDirectories) ? "sub" : "one";
 
-  // Get the return attributes
+  nsCOMPtr<nsILDAPURL> url(do_QueryInterface(uri, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Get/Set the return attributes
   nsCOMPtr<nsISupports> iSupportsMap;
   rv = aArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Require all attributes that are mapped to card properties
-  nsCAutoString returnAttributes;
-  rv = map->GetAllCardAttributes(returnAttributes);
-  NS_ASSERTION(NS_SUCCEEDED(rv), "GetAllCardAttributes failed");
+  PRUint32 returnAttrsCount;
+  char** returnAttrsArray;
+  rv = map->GetAllCardAttributes(&returnAttrsCount, &returnAttrsArray);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = url->SetAttributes(returnAttrsCount,
+                          const_cast<const char**>(returnAttrsArray));
+  // First free the array
+  NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(returnAttrsCount, returnAttrsArray);
+  // Now do the error check
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Also require the objectClass attribute, it is used by
   // nsAbLDAPCard::SetMetaProperties
-  returnAttributes.AppendLiteral(",objectClass");
+  rv = url->AddAttribute("objectClass");
 
   // Get the filter
   nsCOMPtr<nsISupports> supportsExpression;
   rv = aArguments->GetExpression (getter_AddRefs (supportsExpression));
   NS_ENSURE_SUCCESS(rv, rv);
   
   nsCOMPtr<nsIAbBooleanExpression> expression (do_QueryInterface (supportsExpression, &rv));
   nsCAutoString filter;
@@ -474,37 +480,21 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
    *
    * Default the filter string if blank, otherwise it gets
    * set to (objectclass=*) which returns everything. Set 
    * the default to (objectclass=inetorgperson) as this 
    * is the most appropriate default objectclass which is 
    * central to the makeup of the mozilla ldap address book 
    * entries.
    */
-  if(filter.IsEmpty())
+  if (filter.IsEmpty())
   {
     filter.AssignLiteral("(objectclass=inetorgperson)");
   }
-  
-  nsCAutoString host;
-  rv = mDirectoryUrl->GetAsciiHost(host);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  PRInt32 port;
-  rv = mDirectoryUrl->GetPort(&port);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  nsCAutoString dn;
-  rv = mDirectoryUrl->GetDn(dn);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  PRUint32 options;
-  rv = mDirectoryUrl->GetOptions(&options);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
+
   // get the directoryFilter from the directory url and merge it with the user's
   // search filter
   nsCAutoString urlFilter;
   rv = mDirectoryUrl->GetFilter(urlFilter);
   
   // if urlFilter is unset (or set to the default "objectclass=*"), there's
   // no need to AND in an empty search term, so leave prefix and suffix empty
   
@@ -527,38 +517,30 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
     }
   
     searchFilter += filter;
     searchFilter += ')';
   } 
   else
     searchFilter = filter;
 
-  nsCString ldapSearchUrlString;
-  char* _ldapSearchUrlString = 
-    PR_smprintf("ldap%s://%s:%d/%s?%s?%s?%s",
-                (options & nsILDAPURL::OPT_SECURE) ? "s" : "",
-                host.get(),
-                port,
-                dn.get(),
-                returnAttributes.get(),
-                scope.get(),
-                searchFilter.get());
-
-  if (!_ldapSearchUrlString)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  ldapSearchUrlString = _ldapSearchUrlString;
-  PR_smprintf_free(_ldapSearchUrlString);
-
-  nsCOMPtr<nsILDAPURL> url;
-  url = do_CreateInstance(NS_LDAPURL_CONTRACTID, &rv);
+  rv = url->SetFilter(searchFilter);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = url->SetSpec(ldapSearchUrlString);
+  // Now formulate the search string
+  
+  // Get the scope
+  PRInt32 scope;
+  PRBool doSubDirectories;
+  rv = aArguments->GetQuerySubDirectories (&doSubDirectories);
+  NS_ENSURE_SUCCESS(rv, rv);
+  scope = doSubDirectories ? nsILDAPURL::SCOPE_SUBTREE :
+                             nsILDAPURL::SCOPE_ONELEVEL;
+
+  rv = url->SetScope(scope);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // too soon? Do we need a new listener?
   // If we already have a connection, and don't need to re-do it, give it the
   // new search details and go for it...
   if (!redoConnection)
   {
     nsAbQueryLDAPMessageListener *msgListener = 
--- a/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
@@ -41,18 +41,23 @@
 #include "nsAbLDAPListenerBase.h"
 #include "nsIWindowWatcher.h"
 #include "nsIDOMWindow.h"
 #include "nsIAuthPrompt.h"
 #include "nsIStringBundle.h"
 #include "nsIProxyObjectManager.h"
 #include "nsILDAPMessage.h"
 #include "nsILDAPErrors.h"
+#ifdef USE_TK_LOGIN_MANAGER
+#include "nsILoginManager.h"
+#include "nsILoginInfo.h"
+#else
 #include "nsCategoryManagerUtils.h"
 #include "nsComponentManagerUtils.h"
+#endif
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOMCIDInternal.h"
 
 nsAbLDAPListenerBase::nsAbLDAPListenerBase(nsILDAPURL* url,
                                            nsILDAPConnection* connection,
                                            const nsACString &login,
                                            const PRInt32 timeOut) :
   mDirectoryUrl(url), mConnection(connection), mLogin(login),
@@ -318,16 +323,54 @@ nsresult nsAbLDAPListenerBase::OnLDAPMes
 
   if (errCode != nsILDAPErrors::SUCCESS)
   {
     // if the login failed, tell the wallet to forget this password
     //
     if (errCode == nsILDAPErrors::INAPPROPRIATE_AUTH ||
         errCode == nsILDAPErrors::INVALID_CREDENTIALS)
     {
+#ifdef USE_TK_LOGIN_MANAGER
+      // Login failed, so try again - but first remove the existing login(s)
+      // so that the user gets prompted. This may not be the best way of doing
+      // things, we need to review that later.
+
+      nsCOMPtr<nsILoginManager> loginMgr =
+        do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCString spec;
+      rv = mDirectoryUrl->GetSpec(spec);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCString prePath;
+      rv = mDirectoryUrl->GetPrePath(prePath);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      PRUint32 count;
+      nsILoginInfo** logins;
+
+      rv = loginMgr->FindLogins(&count, NS_ConvertUTF8toUTF16(prePath),
+                                EmptyString(),
+                                NS_ConvertUTF8toUTF16(spec), &logins);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Typically there should only be one-login stored for this url, however,
+      // just in case there isn't.
+      for (PRUint32 i = 0; i < count; ++i)
+      {
+        rv = loginMgr->RemoveLogin(logins[i]);
+        if (NS_FAILED(rv))
+        {
+          NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+          return rv;
+        }
+      }
+      NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+#else
       // make sure the wallet service has been created, and in doing so,
       // pass in a login-failed message to tell it to forget this passwd.
       //
       // apparently getting passwords stored in the wallet
       // doesn't require the service to be running, which is why
       // this might not exist yet.
       //
       rv = NS_CreateServicesFromCategory("passwordmanager",
@@ -337,17 +380,20 @@ nsresult nsAbLDAPListenerBase::OnLDAPMes
       {
         NS_ERROR("nsLDAPAutoCompleteSession::ForgetPassword(): error"
                  " creating password manager service");
         // not much to do at this point, though conceivably we could 
         // pop up a dialog telling the user to go manually delete
         // this password in the password manager.
         return rv;
       }
+
       // Login failed, so try again
+#endif
+
       // XXX We should probably pop up an error dialog telling
       // the user that the login failed here, rather than just bringing 
       // up the password dialog again, which is what calling OnLDAPInit()
       // does.
       return OnLDAPInit(nsnull, NS_OK);
     }
 
     // Don't know how to handle this, so use the message error code in
--- a/mailnews/addrbook/test/unit/test_ldap1.js
+++ b/mailnews/addrbook/test/unit/test_ldap1.js
@@ -7,22 +7,17 @@ const kLDAPDirectory = 0; // defined in 
 const kLDAPUriPrefix = "moz-abldapdirectory://";
 const kLDAPTestSpec = "ldap://invalidhost:389//dc=intranet??sub?(objectclass=*)";
 
 function run_test() {
   // Test - Create an LDAP directory
   var abManager = Components.classes["@mozilla.org/abmanager;1"]
                             .getService(Components.interfaces.nsIAbManager);
 
-  var ldapUrl = Components.classes["@mozilla.org/network/ldap-url;1"]
-                          .createInstance(Components.interfaces.nsILDAPURL);
-
-  ldapUrl.spec = kLDAPTestSpec;
-
-  var abUri = abManager.newAddressBook("test", ldapUrl.spec, kLDAPDirectory);
+  var abUri = abManager.newAddressBook("test", kLDAPTestSpec, kLDAPDirectory);
 
   // Test - Check we have the directory.
   var abDir = abManager.getDirectory(kLDAPUriPrefix + abUri)
                        .QueryInterface(Components.interfaces.nsIAbLDAPDirectory);
 
   // Test - Check various fields
   do_check_eq(abDir.dirName, "test");
   do_check_eq(abDir.lDAPURL.spec, kLDAPTestSpec);