When binding to an LDAP server, Thunderbird should attempt GSSAPI (Kerberos) Authentication if available (bug 308118); thanks to Simon Wilkinson <simon@sxw.org.uk>, r=Standard8,bievenu, sr=dmose.
authorDan Mosedale <dmose@mozilla.org>
Thu, 19 Mar 2009 15:31:57 -0700
changeset 2251 73e75f355c99e334cbc03fd6dd5349c39bc531bc
parent 2250 f15173640d7346e4497f59646153646b11137759
child 2252 2ebb18326ffb5c5acf9e57d8322d36ca11054f93
push id1
push userhg
push dateTue, 08 Jul 2014 14:20:55 +0000
treeherdercomm-esr31@1665b2be5642 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8, bievenu, dmose
bugs308118
When binding to an LDAP server, Thunderbird should attempt GSSAPI (Kerberos) Authentication if available (bug 308118); thanks to Simon Wilkinson <simon@sxw.org.uk>, r=Standard8,bievenu, sr=dmose.
directory/xpcom/base/public/nsILDAPOperation.idl
directory/xpcom/base/src/nsLDAPConnection.cpp
directory/xpcom/base/src/nsLDAPOperation.cpp
directory/xpcom/base/src/nsLDAPOperation.h
mail/components/addrbook/content/abCommon.js
mail/components/compose/content/MsgComposeCommands.js
mail/locales/en-US/chrome/messenger/addressbook/pref-directory-add.dtd
mailnews/addrbook/prefs/resources/content/pref-directory-add.js
mailnews/addrbook/prefs/resources/content/pref-directory-add.xul
mailnews/addrbook/public/nsIAbLDAPDirectory.idl
mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl
mailnews/addrbook/src/nsAbLDAPDirectory.cpp
mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h
mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
mailnews/addrbook/src/nsAbLDAPListenerBase.h
mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp
mailnews/compose/resources/content/MsgComposeCommands.js
suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd
--- a/directory/xpcom/base/public/nsILDAPOperation.idl
+++ b/directory/xpcom/base/public/nsILDAPOperation.idl
@@ -17,16 +17,17 @@
  *
  * 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>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * 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
@@ -34,32 +35,33 @@
  * 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 "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 PRUint32 PRIntervalTime;
 
-[scriptable, uuid(b9a7ed9d-adf9-4612-8cb7-cdb5c4ef16fd)]
+[scriptable, uuid(40cc61c6-539b-4157-b0f0-ed81ecd29b8e)]
 interface nsILDAPOperation : nsISupports
 {
     /**
      * The connection this operation is on. 
      * 
      * @exception NS_ERROR_ILLEGAL_VALUE        a NULL pointer was passed in
      */
     readonly attribute nsILDAPConnection connection;
@@ -125,16 +127,36 @@ interface nsILDAPOperation : nsISupports
      * @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
--- a/directory/xpcom/base/src/nsLDAPConnection.cpp
+++ b/directory/xpcom/base/src/nsLDAPConnection.cpp
@@ -22,16 +22,17 @@
  *
  * Contributor(s):
  *   Dan Mosedale <dmose@mozilla.org> (original author)
  *   Leif Hedstrom <leif@netscape.com>
  *   Kipp Hickman <kipp@netscape.com>
  *   Warren Harris <warren@netscape.com>
  *   Dan Matejka <danm@netscape.com>
  *   David Bienvenu <bienvenu@mozilla.org>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * 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
@@ -713,16 +714,21 @@ CheckLDAPOperationResult(nsHashKey *aKey
                 break;
             }
 
             // initialize the message, using a protected method not available
             // through nsILDAPMessage (which is why we need the raw pointer)
             //
             rv = rawMsg->Init(loop->mRawConn, msgHandle);
 
+            // now let the scoping mechanisms provided by nsCOMPtr manage
+            // the reference for us.
+            //
+            msg = rawMsg;
+            
             switch (rv) {
 
             case NS_OK: {
                 PRInt32 errorCode;
                 rawMsg->GetErrorCode(&errorCode);
                 // maybe a version error, e.g., using v3 on a v2 server.
                 // if we're using v3, try v2.
                 //
@@ -739,16 +745,35 @@ CheckLDAPOperationResult(nsHashKey *aKey
                     //
                     rv = operation->SimpleBind(password);
                     if (NS_SUCCEEDED(rv)) {
                         operationFinished = PR_FALSE;
                         // we don't want to notify callers that we're done...
                         return PR_TRUE;
                     }
                 }
+                
+                // 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(
+                      loop->mRawConn->mConnectionHandle, msgHandle, 
+                      &creds, 0);
+
+                    nsCOMPtr <nsILDAPOperation> operation =
+                      static_cast<nsILDAPOperation *>
+                      (static_cast<nsISupports *>(aData));
+
+                    rv = operation->SaslStep(creds->bv_val, creds->bv_len);
+                    if (NS_SUCCEEDED(rv)) {
+                        return PR_TRUE;
+                    }
+                }
             }
             break;
 
             case NS_ERROR_LDAP_DECODING_ERROR:
                 consoleSvc->LogStringMessage(
                     NS_LITERAL_STRING("LDAP: WARNING: decoding error; possible corrupt data received").get());
             NS_WARNING("CheckLDAPOperationResult(): ldaperrno = "
                            "LDAP_DECODING_ERROR after ldap_result()");
@@ -765,21 +790,16 @@ CheckLDAPOperationResult(nsHashKey *aKey
                 //
             NS_ERROR("CheckLDAPOperationResult(): nsLDAPMessage::Init() "
                            "returned unexpected value.");
 
                 // punt and hope things work out better next time around
             return PR_TRUE;
             }
 
-            // now let the scoping mechanisms provided by nsCOMPtr manage
-            // the reference for us.
-            //
-            msg = rawMsg;
-
             // invoke the callback on the nsILDAPOperation corresponding to 
             // this message
             //
         rv = loop->mRawConn->InvokeMessageCallback(msgHandle, msg, 
                                                     operationFinished);
             if (NS_FAILED(rv)) {
             NS_ERROR("CheckLDAPOperationResult(): error invoking message"
                      " callback");
--- a/directory/xpcom/base/src/nsLDAPOperation.cpp
+++ b/directory/xpcom/base/src/nsLDAPOperation.cpp
@@ -17,16 +17,17 @@
  *
  * 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>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * 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
@@ -45,16 +46,17 @@
 #include "nsILDAPModification.h"
 #include "nsIComponentManager.h"
 #include "nsReadableUtils.h"
 #include "nspr.h"
 #include "nsISimpleEnumerator.h"
 #include "nsLDAPControl.h"
 #include "nsILDAPErrors.h"
 #include "nsIClassInfoImpl.h"
+#include "nsIAuthModule.h"
 #include "nsArrayUtils.h"
 
 // Helper function
 static nsresult TranslateLDAPErrorToNSError(const int ldapError)
 {
   switch (ldapError) {
   case LDAP_SUCCESS:
     return NS_OK;
@@ -189,16 +191,110 @@ nsLDAPOperation::GetMessageListener(nsIL
     }
 
     *aMessageListener = mMessageListener;
     NS_IF_ADDREF(*aMessageListener);
 
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsLDAPOperation::SaslBind(const nsACString &service, 
+                          const nsACString &mechanism, 
+                          nsIAuthModule *authModule)
+{
+  nsresult rv;
+  nsCAutoString 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, nsnull, 
+                    NS_ConvertUTF8toUTF16(bindName).get(), nsnull);
+
+  rv = mAuthModule->GetNextToken(nsnull, 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);
+  nsMemory::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 = static_cast<nsLDAPConnection *>
+       (static_cast<nsILDAPConnection *>(mConnection.get()))
+       ->AddPendingOperation(this);
+
+  if (NS_FAILED(rv))
+    (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+nsLDAPOperation::SaslStep(const char *token, PRUint32 tokenLen)
+{
+  nsresult rv;
+  nsCAutoString bindName;
+  struct berval clientCreds;
+  struct berval serverCreds;
+  unsigned int credlen;
+
+  rv = static_cast<nsLDAPConnection *>
+       (static_cast<nsILDAPConnection *>(mConnection.get()))
+       ->RemovePendingOperation(this);
+  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);
+
+  nsMemory::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 = static_cast<nsLDAPConnection *>
+       (static_cast<nsILDAPConnection *>(mConnection.get()))
+       ->AddPendingOperation(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)
 {
     nsresult rv;
     nsCAutoString bindName;
     PRBool originalMsgID = mMsgID;
--- a/directory/xpcom/base/src/nsLDAPOperation.h
+++ b/directory/xpcom/base/src/nsLDAPOperation.h
@@ -17,16 +17,17 @@
  *
  * 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>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * 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
@@ -119,15 +120,17 @@ class nsLDAPOperation : public nsILDAPOp
     static nsresult CopyValues(nsILDAPModification* aMod, berval*** aBValues);
 
     nsCOMPtr<nsILDAPMessageListener> mMessageListener; // results go here
     nsCOMPtr<nsISupports> mClosure;  // private parameter (anything caller desires)
     nsCOMPtr<nsILDAPConnection> mConnection; // connection this op is on
 
     LDAP *mConnectionHandle; // cache connection handle
     nsCString mSavePassword;
+    nsCString mMechanism;
+    nsCOMPtr<nsIAuthModule> mAuthModule;
     PRInt32 mMsgID;          // opaque handle to outbound message for this op
 
     nsCOMPtr<nsIMutableArray> mClientControls;
     nsCOMPtr<nsIMutableArray> mServerControls;
 };
 
 #endif // _nsLDAPOperation_h
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -19,16 +19,17 @@
 # the Initial Developer. All Rights Reserved.
 #
 # Original Author:
 #   Paul Hangas <hangas@netscape.com>
 #
 # Contributor(s):
 #   Seth Spitzer <sspitzer@netscape.com>
 #   Mark Banner <mark@standard8.demon.co.uk>
+#   Simon Wilkinson <simon@sxw.org.uk>
 #
 # 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
@@ -728,16 +729,24 @@ function setupLdapAutocompleteSession()
             //
             try {
 		LDAPSession.login =gPrefs.getComplexValue(
                     autocompleteDirectory + ".auth.dn",
                     Components.interfaces.nsISupportsString).data;
             } catch (ex) {
                 // if we don't have this pref, no big deal
             }
+            
+            try {
+                LDAPSession.saslMechanism = gPrefs.getComplexValue(
+                    autocompleteDirectory + ".auth.saslmech",
+                    Components.interfaces.nsISupportsString).data;
+            } catch (ex) {
+                // if we don't have a mechanism, we'll just use simple binds
+            }
 
             // don't search on non-CJK strings shorter than this
             //
             try {
                 LDAPSession.minStringLength = gPrefs.getIntPref(
                     autocompleteDirectory + ".autoComplete.minStringLength");
             } catch (ex) {
                 // if this pref isn't there, no big deal.  just let
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -18,16 +18,17 @@
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 1998-1999
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   David Bienvenu <bienvenu@nventure.com>
 #   Olivier Parniere BT Global Services / Etat francais Ministere de la Defense
+#   Simon Wilkinson <simon@sxw.org.uk>
 #
 # 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
@@ -858,16 +859,23 @@ function setupLdapAutocompleteSession()
             // get the login to authenticate as, if there is one
             //
             try {
                 LDAPSession.login = getPref(autocompleteDirectory + ".auth.dn", true);
             } catch (ex) {
                 // if we don't have this pref, no big deal
             }
 
+            try {
+                 LDAPSession.saslMechanism = getPref(autocompleteDirectory + 
+		    ".auth.saslmech", true);
+            } catch (ex) {
+                // don't care if we don't have this pref
+            }
+            
             // set the LDAP protocol version correctly
             var protocolVersion;
             try { 
                 protocolVersion = getPref(autocompleteDirectory +
                                           ".protocolVersion");
             } catch (ex) {
                 // if we don't have this pref, no big deal
             }
--- a/mail/locales/en-US/chrome/messenger/addressbook/pref-directory-add.dtd
+++ b/mail/locales/en-US/chrome/messenger/addressbook/pref-directory-add.dtd
@@ -60,13 +60,20 @@
 <!ENTITY scopeOneLevel.label               "One Level">
 <!ENTITY scopeOneLevel.accesskey           "L">
 <!ENTITY scopeSubtree.label                "Subtree">
 <!ENTITY scopeSubtree.accesskey            "S">
 <!ENTITY return.label                      "Don't return more than">
 <!ENTITY return.accesskey                  "r">
 <!ENTITY results.label                     "results">
 <!ENTITY offlineText.label                 "You can download a local copy of this directory so that it is available for use when you are working offline.">
+<!ENTITY saslMechanism.label               "Login method: ">
+<!ENTITY saslMechanism.accesskey           "m">
+<!ENTITY saslOff.label                     "Simple">
+<!ENTITY saslOff.accesskey                 "l">
+<!ENTITY saslGSSAPI.label                  "Kerberos (GSSAPI)">
+<!ENTITY saslGSSAPI.accesskey              "K">
+
 <!-- Localization note: this is here because the width of the dialog 
      is determined by the width of the base DN box; and that is likely
      to vary somewhat with the language.
 -->
 <!ENTITY newDirectoryWidth                 "36em">
--- a/mailnews/addrbook/prefs/resources/content/pref-directory-add.js
+++ b/mailnews/addrbook/prefs/resources/content/pref-directory-add.js
@@ -195,16 +195,26 @@ function fillSettings()
     switch(ldapUrl.scope) {
     case Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL:
       sub.radioGroup.selectedItem = document.getElementById("one");
       break;
     default:
       sub.radioGroup.selectedItem = sub;
       break;
     }
+    
+    var sasl = document.getElementById("saslMechanism");
+    switch (gCurrentDirectory.saslMechanism) {
+    case "GSSAPI":
+      sasl.selectedItem = document.getElementById("GSSAPI");
+      break;
+    default:
+      sasl.selectedItem = document.getElementById("Simple");
+      break;
+    }
 
     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);
@@ -292,16 +302,17 @@ function onAccept()
     var pref_string_title = "";
 
     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;
+    var saslMechanism = "";
     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))
@@ -309,27 +320,30 @@ function onAccept()
     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 (document.getElementById("GSSAPI").selected) {
+        saslMechanism = "GSSAPI";
+      }
 
       // check if we are modifying an existing directory or adding a new directory
       if (gCurrentDirectory) {
         gCurrentDirectory.dirName = description;
         gCurrentDirectory.lDAPURL = ldapUrl.QueryInterface(Components.interfaces.nsILDAPURL);
         window.opener.gNewServerString = gCurrentDirectory.dirPrefId;
       }
       else { // adding a new directory
@@ -345,16 +359,17 @@ function onAccept()
       // give us back the new directory we just created - so go find it from
       // rdf so we can set a few final things up on it.
       var theDirectory = RDF.GetResource("moz-abldapdirectory://" +
                                          window.opener.gNewServerString)
         .QueryInterface(Components.interfaces.nsIAbLDAPDirectory);
 
       theDirectory.maxHits = results;
       theDirectory.authDn = document.getElementById("login").value;
+      theDirectory.saslMechanism = saslMechanism;
 
       window.opener.gNewServer = description;
       // set window.opener.gUpdate to true so that LDAP Directory Servers
       // dialog gets updated
       window.opener.gUpdate = true; 
     } else {
       var addressBookBundle = document.getElementById("bundle_addressBook");
 
--- a/mailnews/addrbook/prefs/resources/content/pref-directory-add.xul
+++ b/mailnews/addrbook/prefs/resources/content/pref-directory-add.xul
@@ -161,16 +161,29 @@
                  </radiogroup>
                </row>
                <row>
                  <label value="&searchFilter.label;" 
                        accesskey="&searchFilter.accesskey;"
                        control="search"/>
                  <textbox id="search" multiline="true" flex="1" disableiflocked="true"/>
               </row>
+              <row>
+                <label value="&saslMechanism.label;" control="saslMechanism" accesskey="&saslMechanism.accesskey;"/>
+                <menulist id="saslMechanism">
+                  <menupopup>
+                    <menuitem id="Simple" value="" 
+                              label="&saslOff.label;"
+                              accesskey="&saslOff.accesskey;"/>
+                    <menuitem id="GSSAPI" value="GSSAPI" 
+                              label="&saslGSSAPI.label;"
+                              accesskey="&saslGSSAPI.accesskey;"/>
+                  </menupopup>
+                </menulist>
+              </row>
             </rows>
           </grid>
       </tabpanels>
     </tabbox>
   </vbox>
   
 </dialog>
         
--- a/mailnews/addrbook/public/nsIAbLDAPDirectory.idl
+++ b/mailnews/addrbook/public/nsIAbLDAPDirectory.idl
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Oracle Corporation
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mosedale <dan.mosedale@oracle.com>
  *   Jeremy Laine <jeremy.laine@m4x.org>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -49,17 +50,17 @@ interface nsILDAPURL;
 #define kLDAPDirectoryRoot         "moz-abldapdirectory://"
 #define kLDAPDirectoryRootLen      22
 %}
 
 /**
  * XXX This should really inherit from nsIAbDirectory, and some day it will.
  * But for now, doing that complicates implementation.
  */
-[scriptable, uuid(2f689622-3d73-43aa-8580-6a7ba232be2b)]
+[scriptable, uuid(90dde295-e354-4d58-Add8-f9b29a95942d)]
 interface nsIAbLDAPDirectory : nsISupports
 {
   /**
    * If set, these arrays of nsILDAPControls are passed through to the
    * nsILDAPOperation that searchExt is called on.
    */
   attribute nsIMutableArray searchServerControls;
   attribute nsIMutableArray searchClientControls;
@@ -70,16 +71,24 @@ interface nsIAbLDAPDirectory : nsISuppor
   attribute ACString replicationFileName;
 
   /**
    * The version of LDAP protocol in use.
    */
   attribute unsigned long protocolVersion;
 
   /**
+   * The SASL mechanism to use to authenticate to the LDAP server
+   * If this is an empty string, then a simple bind will be performed
+   * A non-zero string is assumed to be the name of the SASL mechanism.
+   * Currently the only supported mechanism is GSSAPI
+   */
+  attribute ACString saslMechanism;
+  
+  /**
    * The AuthDN to use to access the server.
    */
   attribute AUTF8String authDn;
 
   /**
    * The maximum number of matches that the server will return per a search.
    */
   attribute long maxHits;
--- a/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl
+++ b/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mosedale <dmose@netscape.com> (Original Author)
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -44,17 +45,17 @@
 interface nsILDAPURL;   
 interface nsILDAPAutoCompFormatter;
 interface nsIMutableArray;
 
 /**
  * Extends nsIAutoCompleteSession to have various LDAP-specific parameters.
  * and output format.
  */
-[scriptable, uuid(aced5f6f-013d-4e21-97a6-9da3779c8663)]
+[scriptable, uuid(d6a64827-6206-4070-9bf9-24578df0afe2)]
 interface nsILDAPAutoCompleteSession : nsIAutoCompleteSession {
 
     /**
      * A template used to construct the RFC 1960 LDAP search filter to use 
      * while autocompleting.  
      *
      * The authoritative documentation for the format in use can be found at 
      * at <http://docs.iplanet.com/docs/manuals/dirsdk/csdk41/html/filter.htm>.
@@ -121,16 +122,25 @@ interface nsILDAPAutoCompleteSession : n
     /**
      * "Login as..." this ID.  Currently, this must be specified as a DN.
      * In the future, we may support userid and/or email address as well.
      * If unset, bind anonymously.
      */
     attribute AUTF8String login;
 
     /**
+     * SASL Mechanism to use to perform bind. If unset, a simple bind will
+     * be performed.
+     *
+     * @exception NS_ERROR_OUT_OF_MEMORY    Getter couldn't allocate string
+     * @exception NS_ERROR_NULL_POINTER   null pointer passed to getter
+     */
+    attribute ACString saslMechanism;
+    
+    /**
      * What version of the LDAP protocol should be used?  Allowed version
      * number constants are defined in nsILDAPConnection.idl.
      *
      * @exception NS_ERROR_ILLEGAL_VALUE  illegal version num passed to setter
      * @exception NS_ERROR_NULL_POINTER   null pointer passed to getter
      */
     attribute unsigned long version;
 
--- a/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPDirectory.cpp
@@ -20,16 +20,17 @@
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Seth Spitzer <sspitzer@netscape.com>
  *   Dan Mosedale <dmose@netscape.com>
  *   Paul Sandoz <paul.sandoz@sun.com>
  *   Mark Banner <bugzilla@standard8.plus.com>
  *   Jeremy Laine <jeremy.laine@m4x.org>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -641,16 +642,26 @@ NS_IMETHODIMP nsAbLDAPDirectory::GetAuth
 
 NS_IMETHODIMP nsAbLDAPDirectory::SetAuthDn(const nsACString &aAuthDn)
 {
   // XXX We should cancel any existing LDAP connections here and
   // be ready to re-initialise them with the new auth details.
   return SetStringValue("auth.dn", aAuthDn);
 }
 
+NS_IMETHODIMP nsAbLDAPDirectory::GetSaslMechanism(nsACString &aSaslMechanism)
+{
+  return GetStringValue("auth.saslmech", EmptyCString(), aSaslMechanism);
+}
+
+NS_IMETHODIMP nsAbLDAPDirectory::SetSaslMechanism(const nsACString &aSaslMechanism)
+{
+  return SetStringValue("auth.saslmech", aSaslMechanism);
+}
+
 NS_IMETHODIMP nsAbLDAPDirectory::GetLastChangeNumber(PRInt32 *aLastChangeNumber)
 {
   return GetIntValue("lastChangeNumber", -1, aLastChangeNumber);
 }
 
 NS_IMETHODIMP nsAbLDAPDirectory::SetLastChangeNumber(PRInt32 aLastChangeNumber)
 {
   return SetIntValue("lastChangeNumber", aLastChangeNumber);
--- a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
@@ -20,16 +20,17 @@
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Seth Spitzer <sspitzer@netscape.com>
  *   Dan Mosedale <dmose@netscape.com>
  *   Paul Sandoz <paul.sandoz@sun.com>
  *   Mark Banner <bugzilla@standard8.plus.com>
  *   Jeremy Laine <jeremy.laine@m4x.org>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -73,16 +74,17 @@ public:
   nsAbQueryLDAPMessageListener(nsIAbDirectoryQueryResultListener* resultListener,
                                nsILDAPURL* directoryUrl,
                                nsILDAPURL* searchUrl,
                                nsILDAPConnection* connection,
                                nsIAbDirectoryQueryArguments* queryArguments,
                                nsIMutableArray* serverSearchControls,
                                nsIMutableArray* clientSearchControls,
                                const nsACString &login,
+                               const nsACString &mechanism,
                                const PRInt32 resultLimit = -1,
                                const PRInt32 timeOut = 0);
   virtual ~nsAbQueryLDAPMessageListener ();
 
   // nsILDAPMessageListener
   NS_IMETHOD OnLDAPMessage(nsILDAPMessage *aMessage);
 
 protected:
@@ -116,29 +118,31 @@ nsAbQueryLDAPMessageListener::nsAbQueryL
         nsIAbDirectoryQueryResultListener *resultListener,
         nsILDAPURL* directoryUrl,
         nsILDAPURL* searchUrl,
         nsILDAPConnection* connection,
         nsIAbDirectoryQueryArguments* queryArguments,
         nsIMutableArray* serverSearchControls,
         nsIMutableArray* clientSearchControls,
         const nsACString &login,
+        const nsACString &mechanism,
         const PRInt32 resultLimit,
         const PRInt32 timeOut) :
   nsAbLDAPListenerBase(directoryUrl, connection, login, timeOut),
   mSearchUrl(searchUrl),
   mResultListener(resultListener),
   mQueryArguments(queryArguments),
   mResultLimit(resultLimit),
   mFinished(PR_FALSE),
   mCanceled(PR_FALSE),
   mWaitingForPrevQueryToFinish(PR_FALSE),
   mServerSearchControls(serverSearchControls),
   mClientSearchControls(clientSearchControls)
 {
+  mSaslMechanism.Assign(mechanism);
 }
 
 nsAbQueryLDAPMessageListener::~nsAbQueryLDAPMessageListener ()
 {
 }
 
 nsresult nsAbQueryLDAPMessageListener::Cancel ()
 {
@@ -375,31 +379,36 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
   nsCOMPtr<nsILDAPURL> currentUrl;
   rv = directory->GetLDAPURL(getter_AddRefs(currentUrl));
   NS_ENSURE_SUCCESS(rv, rv);
   
   nsCAutoString login;
   rv = directory->GetAuthDn(login);
   NS_ENSURE_SUCCESS(rv, rv);
   
+  nsCAutoString saslMechanism;
+  rv = directory->GetSaslMechanism(saslMechanism);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
   PRUint32 protocolVersion;
   rv = directory->GetProtocolVersion(&protocolVersion);
   NS_ENSURE_SUCCESS(rv, rv);
   
   // To do:
   // Ensure query is stopped
   // If connection params have changed re-create connection
   // else reuse existing connection
   
   PRBool redoConnection = PR_FALSE;
   
   if (!mConnection || !mDirectoryUrl)
   {
     mDirectoryUrl = currentUrl;
     mCurrentLogin = login;
+    mCurrentMechanism = saslMechanism;
     mCurrentProtocolVersion = protocolVersion;
     redoConnection = PR_TRUE;
   }
   else
   {
     PRBool equal;
     rv = mDirectoryUrl->Equals(currentUrl, &equal);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -407,26 +416,30 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
     nsCString spec;
     mDirectoryUrl->GetSpec(spec);
     currentUrl->GetSpec(spec);
 
     if (!equal)
     {
       mDirectoryUrl = currentUrl;
       mCurrentLogin = login;
+      mCurrentMechanism = saslMechanism;
       mCurrentProtocolVersion = protocolVersion;
       redoConnection = PR_TRUE;
     }
     else
     {
       // Has login or version changed?
-      if (login != mCurrentLogin || protocolVersion != mCurrentProtocolVersion)
+      if (login != mCurrentLogin ||
+          saslMechanism != mCurrentMechanism ||
+          protocolVersion != mCurrentProtocolVersion)
       {
         redoConnection = PR_TRUE;
         mCurrentLogin = login;
+        mCurrentMechanism = saslMechanism;
         mCurrentProtocolVersion = protocolVersion;
       }
     }
   }
 
   nsCOMPtr<nsIURI> uri;
   rv = mDirectoryUrl->Clone(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -575,17 +588,18 @@ NS_IMETHODIMP nsAbLDAPDirectoryQuery::Do
     do_QueryInterface((nsIAbDirectoryQuery*)this, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Initiate LDAP message listener
   nsAbQueryLDAPMessageListener* _messageListener =
     new nsAbQueryLDAPMessageListener(resultListener, mDirectoryUrl, url,
                                      mConnection, aArguments,
                                      serverSearchControls, clientSearchControls,
-                                     mCurrentLogin, aResultLimit, aTimeOut);
+                                     mCurrentLogin, mCurrentMechanism,
+                                     aResultLimit, aTimeOut);
   if (_messageListener == NULL)
     return NS_ERROR_OUT_OF_MEMORY;
   
   mListener = _messageListener;
   *_retval = 1;
 
   // Now lets initialize the LDAP connection properly. We'll kick
   // off the bind operation in the callback function, |OnLDAPInit()|.
--- a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.h
@@ -63,14 +63,15 @@ public:
 protected:
   nsCOMPtr<nsILDAPMessageListener> mListener;
 
 private:
   nsCOMPtr<nsILDAPConnection> mConnection;
   nsCOMPtr<nsILDAPURL> mDirectoryUrl;
   nsCOMArray<nsIAbDirSearchListener> mListeners;
   nsCString mCurrentLogin;
+  nsCString mCurrentMechanism;
   PRUint32 mCurrentProtocolVersion;
 
   PRBool mInitialized;
 };
 
 #endif // nsAbLDAPDirectoryQuery_h__
--- a/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPListenerBase.cpp
@@ -18,16 +18,17 @@
  * Sun Microsystems, Inc.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Paul Sandoz <paul.sandoz@sun.com>
  *   Dan Mosedale <dmose@mozilla.org>
  *   Mark Banner <mark@standard8.demon.co.uk>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -102,17 +103,17 @@ NS_IMETHODIMP nsAbLDAPListenerBase::OnLD
   if (NS_FAILED(aStatus))
   {
     InitFailed();
     return NS_OK;
   }
 
   // If mLogin is set, we're expected to use it to get a password.
   //
-  if (!mLogin.IsEmpty())
+  if (!mLogin.IsEmpty() && !mSaslMechanism.Equals(NS_LITERAL_CSTRING("GSSAPI")))
   {
     // get the string bundle service
     //
     nsCOMPtr<nsIStringBundleService> stringBundleSvc = 
       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     if (NS_FAILED(rv))
     {
       NS_ERROR("nsAbLDAPListenerBase::OnLDAPInit():"
@@ -289,16 +290,40 @@ NS_IMETHODIMP nsAbLDAPListenerBase::OnLD
   rv = mOperation->Init(mConnection, proxyListener, nsnull);
   if (NS_FAILED(rv))
   {
     NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to Initialise operation");
     InitFailed();
     return rv;
   }
 
+  // Try non-password mechanisms first
+  if (mSaslMechanism.Equals(NS_LITERAL_CSTRING("GSSAPI")))
+  {
+    nsCAutoString service;
+    rv = mDirectoryUrl->GetAsciiHost(service);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    service.Insert(NS_LITERAL_CSTRING("ldap@"), 0);
+
+    nsCOMPtr<nsIAuthModule> authModule =
+      do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sasl-gssapi", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mOperation->SaslBind(service, mSaslMechanism, authModule);
+    if (NS_FAILED(rv))
+    {
+      NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): "
+               "failed to perform GSSAPI bind");
+      mOperation = 0; // Break Listener -> Operation -> Listener ref cycle
+      InitFailed();
+    }
+    return rv;
+  }
+
   // Bind
   rv = mOperation->SimpleBind(NS_ConvertUTF16toUTF8(passwd));
   if (NS_FAILED(rv))
   {
     NS_ERROR("nsAbLDAPMessageBase::OnLDAPInit(): failed to perform bind operation");
     mOperation = 0; // Break Listener->Operation->Listener reference cycle
     InitFailed();
   }
--- a/mailnews/addrbook/src/nsAbLDAPListenerBase.h
+++ b/mailnews/addrbook/src/nsAbLDAPListenerBase.h
@@ -71,16 +71,17 @@ protected:
 
   // Called to start off the required task after a bind.
   virtual nsresult DoTask() = 0;
 
   nsCOMPtr<nsILDAPURL> mDirectoryUrl;
   nsCOMPtr<nsILDAPOperation> mOperation;        // current ldap op
   nsILDAPConnection* mConnection;
   nsCString mLogin;
+  nsCString mSaslMechanism;
   PRInt32 mTimeOut;
   PRBool mBound;
   PRBool mInitialized;
 
   PRLock* mLock;
 };
 
 #endif
--- a/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
+++ b/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Rajiv Dayal <rdayal@netscape.com>
  * Portions created by the Initial Developer are Copyright (C) 2002
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Dan Mosedale <dan.mosedale@oracle.com>
  *  Mark Banner <bugzilla@standard8.demon.co.uk>
+ *  Simon Willkinson <simon@sxw.org.uk>
  *
  * 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
@@ -97,16 +98,22 @@ NS_IMETHODIMP nsAbLDAPProcessReplication
     return rv;
   }
 
   rv = mDirectory->GetAuthDn(mLogin);
   if (NS_FAILED(rv)) {
     mQuery = nsnull;
     return rv;
   }
+  
+  rv = mDirectory->GetSaslMechanism(mSaslMechanism);
+  if (NS_FAILED(rv)) {
+    mQuery = nsnull;
+    return rv;
+  }
 
   mInitialized = PR_TRUE;
 
   return rv;
 }
 
 NS_IMETHODIMP nsAbLDAPProcessReplicationData::GetReplicationState(PRInt32 *aReplicationState) 
 {
--- a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp
+++ b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp
@@ -18,16 +18,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dan Mosedale <dmose@netscape.com> (Original Author)
  *   Leif Hedstrom <leif@netscape.com>
+ *   Simon Wilkinson <simon@sxw.org.uk>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -1242,16 +1243,31 @@ nsLDAPAutoCompleteSession::SetLogin(cons
 }
 NS_IMETHODIMP
 nsLDAPAutoCompleteSession::GetLogin(nsACString & aLogin) 
 {
     aLogin = mLogin;
     return NS_OK;
 }
 
+// attribute ACString saslMechanism
+
+NS_IMETHODIMP
+nsLDAPAutoCompleteSession::SetSaslMechanism(const nsACString & aSaslMechanism)
+{
+    mSaslMechanism.Assign(aSaslMechanism);
+    return NS_OK;
+}
+NS_IMETHODIMP
+nsLDAPAutoCompleteSession::GetSaslMechanism(nsACString & aSaslMechanism)
+{
+    aSaslMechanism.Assign(mSaslMechanism);
+    return NS_OK;
+}
+
 // attribute unsigned long version;
 NS_IMETHODIMP 
 nsLDAPAutoCompleteSession::GetVersion(PRUint32 *aVersion)
 {
     if (!aVersion) {
         return NS_ERROR_NULL_POINTER;
     }
 
--- a/mailnews/compose/resources/content/MsgComposeCommands.js
+++ b/mailnews/compose/resources/content/MsgComposeCommands.js
@@ -844,16 +844,24 @@ function setupLdapAutocompleteSession()
             //
             try {
                 LDAPSession.login = sPrefs.getComplexValue(
                     autocompleteDirectory + ".auth.dn",
                     Components.interfaces.nsISupportsString).data;
             } catch (ex) {
                 // if we don't have this pref, no big deal
             }
+            
+            try {
+                LDAPSession.saslMechanism = sPrefs.getComplexValue(
+                    autocompleteDirectory + ".auth.saslmech",
+                    Components.interfaces.nsISupportsString).data;
+            } catch (ex) {
+                // don't care if we don't have this pref
+            } 
 
             // set the LDAP protocol version correctly
             var protocolVersion;
             try {
                 protocolVersion = sPrefs.getCharPref(autocompleteDirectory +
                                                       ".protocolVersion");
             } catch (ex) {
                 // if we don't have this pref, no big deal
--- a/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd
+++ b/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd
@@ -60,13 +60,20 @@
 <!ENTITY scopeOneLevel.label               "One Level">
 <!ENTITY scopeOneLevel.accesskey           "L">
 <!ENTITY scopeSubtree.label                "Subtree">
 <!ENTITY scopeSubtree.accesskey            "S">
 <!ENTITY return.label                      "Don't return more than">
 <!ENTITY return.accesskey                  "r">
 <!ENTITY results.label                     "results">
 <!ENTITY offlineText.label                 "You can download a local copy of this directory so that it is available for use when you are working offline.">
+<!ENTITY saslMechanism.label               "Login method: ">
+<!ENTITY saslMechanism.accesskey           "m">
+<!ENTITY saslOff.label                     "Simple">
+<!ENTITY saslOff.accesskey		   "l">
+<!ENTITY saslGSSAPI.label                  "Kerberos (GSSAPI)">
+<!ENTITY saslGSSAPI.accesskey		   "K">
+
 <!-- Localization note: this is here because the width of the dialog 
      is determined by the width of the base DN box; and that is likely
      to vary somewhat with the language.
 -->
 <!ENTITY newDirectoryWidth                 "36em">