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 idunknown
push userunknown
push dateunknown
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">