You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2013/03/22 20:34:17 UTC

svn commit: r1459947 [1/2] - in /manifoldcf/trunk: ./ connectors/livelink/ connectors/livelink/build-stub/src/main/java/com/opentext/api/ connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/ connectors/livelink...

Author: kwright
Date: Fri Mar 22 19:34:17 2013
New Revision: 1459947

URL: http://svn.apache.org/r1459947
Log:
Add support for Livelink LAPI connections via SSL.  Fix for CONNECTORS-664.

Added:
    manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSSLNotAvailableException.java
      - copied unchanged from r1459944, manifoldcf/branches/CONNECTORS-664/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSSLNotAvailableException.java
Modified:
    manifoldcf/trunk/   (props changed)
    manifoldcf/trunk/CHANGES.txt
    manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSession.java
    manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLValue.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LLSERVER.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LiveLinkParameters.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/livelink/common_en_US.properties
    manifoldcf/trunk/connectors/livelink/connector/src/main/native2ascii/org/apache/manifoldcf/crawler/connectors/livelink/common_ja_JP.properties
    manifoldcf/trunk/connectors/livelink/lib-proprietary/README.txt
    manifoldcf/trunk/connectors/livelink/proprietary-library-instructions.txt
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/system/ManifoldCF.java
    manifoldcf/trunk/framework/ui-core/src/main/native2ascii/org/apache/manifoldcf/ui/i18n/common_en_US.properties
    manifoldcf/trunk/framework/ui-core/src/main/native2ascii/org/apache/manifoldcf/ui/i18n/common_ja_JP.properties
    manifoldcf/trunk/site/src/documentation/content/xdocs/en_US/how-to-build-and-deploy.xml
    manifoldcf/trunk/site/src/documentation/content/xdocs/ja_JP/how-to-build-and-deploy.xml

Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
  Merged /manifoldcf/branches/CONNECTORS-664:r1457694-1459944

Modified: manifoldcf/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/CHANGES.txt?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/CHANGES.txt (original)
+++ manifoldcf/trunk/CHANGES.txt Fri Mar 22 19:34:17 2013
@@ -3,6 +3,9 @@ $Id$
 
 ======================= 1.2-dev =====================
 
+CONNECTORS-644: Add support for Livelink SSL connections via LAPI.
+(David Morana, Karl Wright)
+
 CONNECTORS-663: Add support for minimal crawling.
 (Maciej Li¿ewski, Karl Wright)
 

Modified: manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSession.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSession.java?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSession.java (original)
+++ manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLSession.java Fri Mar 22 19:34:17 2013
@@ -27,6 +27,10 @@ public class LLSession extends LLConnect
   {
     super(server,port,null,null);
   }
-  
+
+  public static void GetCARootCerts(String path, LLValue something)
+  {
+  }
+
 }
 

Modified: manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLValue.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLValue.java?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLValue.java (original)
+++ manifoldcf/trunk/connectors/livelink/build-stub/src/main/java/com/opentext/api/LLValue.java Fri Mar 22 19:34:17 2013
@@ -24,6 +24,9 @@ import java.util.*;
 */
 public class LLValue
 {
+  public static final int LL_TRUE = 1;
+  public static final int LL_FALSE = 0;
+  
   public LLValue()
   {
   }
@@ -63,8 +66,19 @@ public class LLValue
     return null;
   }
   
-  public void add(String attributeName, int intValue)
+  public int add(String attributeName, int intValue)
+  {
+    return 0;
+  }
+
+  public int add(String attributeName, String strValue)
   {
+    return 0;
+  }
+
+  public int add(String attributeName, LLValue llValue)
+  {
+    return 0;
   }
   
   public boolean isTable()

Modified: manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LLSERVER.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LLSERVER.java?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LLSERVER.java (original)
+++ manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LLSERVER.java Fri Mar 22 19:34:17 2013
@@ -19,9 +19,13 @@
 package org.apache.manifoldcf.crawler.connectors.livelink;
 
 import org.apache.manifoldcf.core.interfaces.*;
+import org.apache.manifoldcf.core.common.Base64;
 import org.apache.manifoldcf.agents.interfaces.*;
 import org.apache.manifoldcf.crawler.interfaces.*;
 import org.apache.manifoldcf.crawler.system.Logging;
+import org.apache.manifoldcf.crawler.system.ManifoldCF;
+
+import java.io.*;
 
 import com.opentext.api.LLSession;
 import com.opentext.api.LLValue;
@@ -40,33 +44,164 @@ public class LLSERVER
 {
   public static final String _rcsid = "@(#)$Id: LLSERVER.java 988245 2010-08-23 18:39:35Z kwright $";
 
-  private String LLServer;
-  private int LLPort;
-  private String LLUser;
-  private String LLPwd;
-  private LLValue LLConfig;
-  private LLSession session;
-
-
-  public LLSERVER()
-  {
-
-    session = null;
-  }
-
-  public LLSERVER(String server, int port, String user, String pwd)
+  private final boolean useHttp;
+  private final boolean useSSL;
+  private final String LLServer;
+  private final int LLPort;
+  private final String LLUser;
+  private final String LLPwd;
+  private final String httpCgiPath;
+  private final String httpNtlmDomain;
+  private final String httpNtlmUser;
+  private final String httpNtlmPassword;
+  private final IKeystoreManager keystore;
+  
+  private LLSession session = null;
+  private File certFolder = null;
+
+
+  public LLSERVER(boolean useHttp, boolean useSSL, String server, int port, String user, String pwd,
+    String httpCgiPath, String httpNtlmDomain, String httpNtlmUser, String httpNtlmPassword,
+    IKeystoreManager keystoreManager)
+    throws ManifoldCFException
   {
+    this.useHttp = useHttp;
+    this.useSSL = useSSL;
     LLServer = server;
     LLPort = port;
     LLUser = user;
     LLPwd = pwd;
+    this.httpCgiPath = httpCgiPath;
+    this.httpNtlmDomain = httpNtlmDomain;
+    this.httpNtlmUser = httpNtlmUser;
+    this.httpNtlmPassword = httpNtlmPassword;
+    this.keystore = keystoreManager;
 
     connect();
   }
 
   private void connect()
+    throws ManifoldCFException
   {
-    session = new LLSession (this.LLServer, this.LLPort, "", this.LLUser, this.LLPwd, null);
+    try
+    {
+    
+      LLValue configuration;
+
+      if (useHttp)
+      {
+        boolean useNTLM;
+        String userNameAndDomain;
+
+        if (httpNtlmDomain != null)
+        {
+          useNTLM = true;
+          userNameAndDomain = httpNtlmUser + "@" + httpNtlmDomain;
+        }
+        else
+        {
+          useNTLM = false;
+          userNameAndDomain = httpNtlmUser;
+        }
+        configuration = new LLValue();
+        configuration.setAssoc();
+        configuration.add("Encoding","UTF-8");
+        configuration.add("LivelinkCGI", httpCgiPath);
+        if (userNameAndDomain != null && userNameAndDomain.length() > 0)
+        {
+          configuration.add("HTTPUserName", userNameAndDomain);
+          configuration.add("HTTPPassword", httpNtlmPassword);
+        }
+        if (useNTLM)
+          configuration.add("EnableNTLM", LLValue.LL_TRUE);
+        else
+          configuration.add("EnableNTLM", LLValue.LL_FALSE);
+
+        if (useSSL)
+        {
+          configuration.add("HTTPS", LLValue.LL_TRUE);
+          // Create the place to put the certs
+          createCertFolder();
+          if (keystore != null)
+          {
+            // Now, write the certs themselves
+            String[] aliases = keystore.getContents();
+            for (String alias : aliases)
+            {
+              java.security.cert.Certificate cert = keystore.getCertificate(alias);
+              byte[] certData = cert.getEncoded();
+              File fileName = new File(certFolder,ManifoldCF.safeFileName(alias) + ".cer");
+              OutputStream fos = new FileOutputStream(fileName);
+              try
+              {
+                Writer osw = new OutputStreamWriter(fos,"utf-8");
+                try
+                {
+                  String certBase64 = new Base64().encodeByteArray(certData);
+                  osw.write("-----BEGIN CERTIFICATE-----\n");
+                  int index = 0;
+                  while (true)
+                  {
+                    if (certBase64.length() - index > 64)
+                    {
+                      osw.write(certBase64.substring(index,index+64) + "\n");
+                      index += 64;
+                    }
+                    else
+                    {
+                      osw.write(certBase64.substring(index) + "\n");
+                      break;
+                    }
+                  }
+                  osw.write("-----END CERTIFICATE-----\n");
+                }
+                finally
+                {
+                  osw.flush();
+                }
+              }
+              finally
+              {
+                fos.flush();
+                fos.close();
+              }
+            }
+          }
+          LLValue rootCACertList = new LLValue();
+          LLSession.GetCARootCerts(certFolder.toString(),rootCACertList);
+          configuration.add("CARootCerts", rootCACertList);
+        }
+      }
+      else
+        configuration = null;
+
+      session = new LLSession (this.LLServer, this.LLPort, "", this.LLUser, this.LLPwd, configuration);
+    }
+    catch (IOException e)
+    {
+      releaseCertFolder();
+      throw new ManifoldCFException("IO Exception writing cert files: "+e.getMessage(),e);
+    }
+    catch (java.security.cert.CertificateEncodingException e)
+    {
+      releaseCertFolder();
+      throw new ManifoldCFException("Bad certificate: "+e.getMessage(),e);
+    }
+    catch (ManifoldCFException e)
+    {
+      releaseCertFolder();
+      throw e;
+    }
+    catch (Error e)
+    {
+      releaseCertFolder();
+      throw e;
+    }
+    catch (RuntimeException e)
+    {
+      releaseCertFolder();
+      throw e;
+    }
   }
 
 
@@ -76,10 +211,29 @@ public class LLSERVER
   */
   public void disconnect()
   {
-
+    releaseCertFolder();
     session = null;
   }
 
+  /** Create temporary session-bound cert directory.
+  */
+  protected void createCertFolder()
+    throws ManifoldCFException
+  {
+    certFolder = ManifoldCF.createTempDir("llcrt_",".d");
+    ManifoldCF.addFile(certFolder);
+  }
+  
+  /** Release temporary session-bound cert directory.
+  */
+  protected void releaseCertFolder()
+  {
+    if (certFolder != null)
+    {
+      ManifoldCF.deleteFile(certFolder);
+      certFolder = null;
+    }
+  }
 
   /**
   * Returns the server name where the Livelink

Modified: manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LiveLinkParameters.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LiveLinkParameters.java?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LiveLinkParameters.java (original)
+++ manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LiveLinkParameters.java Fri Mar 22 19:34:17 2013
@@ -26,12 +26,23 @@ public class LiveLinkParameters
 {
   public static final String _rcsid = "@(#)$Id: LiveLinkParameters.java 988245 2010-08-23 18:39:35Z kwright $";
 
+  // These parameters are for ingestion: picking up a document after we discover it through LAPI
   /** Ingestion CGI protocol */
   public final static String ingestProtocol = "Protocol";
   /** Ingestion CGI port **/
   public final static String ingestPort = "Port";
   /** Ingestion CGI path (path to fetch document from for ingestion) */
   public final static String ingestCgiPath = "CGI path";
+  /** NTLM username */
+  public final static String ingestNtlmUsername = "NTLM user name";
+  /** NTLM password */
+  public final static String ingestNtlmPassword = "NTLM password";
+  /** NTLM domain (set if NTLM desired) */
+  public final static String ingestNtlmDomain = "NTLM domain";
+  /** Livelink SSL keystore */
+  public final static String ingestKeystore = "Livelink SSL keystore";
+  
+  // These parameters are for viewing: constructing a URL the user can use to view the document
   /** View CGI protocol */
   public final static String viewProtocol = "View protocol";
   /** View CGI server name */
@@ -40,6 +51,10 @@ public class LiveLinkParameters
   public final static String viewPort = "View port";
   /** View CGI path (path to use for viewing) */
   public final static String viewCgiPath = "View CGI path";
+  
+  // These parameters are for LAPI
+  /** Connection options; choices are "internal", "http", "https" */
+  public final static String serverProtocol = "Server protocol";
   /** Server name */
   public final static String serverName = "Server name";
   /** Server port */
@@ -48,14 +63,19 @@ public class LiveLinkParameters
   public final static String serverUsername = "Server user name";
   /** Server password */
   public final static String serverPassword = "Server password";
-  /** NTLM username */
-  public final static String ntlmUsername = "NTLM user name";
-  /** NTLM password */
-  public final static String ntlmPassword = "NTLM password";
-  /** NTLM domain (set if NTLM desired) */
-  public final static String ntlmDomain = "NTLM domain";
-  /** Livelink SSL keystore */
-  public final static String livelinkKeystore = "Livelink SSL keystore";
+  /** Server CGI path (path to use for viewing) */
+  public final static String serverHTTPCgiPath = "Server HTTP CGI path";
+  /** Server domain, if NTLM */
+  public final static String serverHTTPNTLMDomain = "Server HTTP NTLM domain";
+  /** Server HTTP user */
+  public final static String serverHTTPNTLMUsername = "Server HTTP NTLM user name";
+  /** Server password */
+  public final static String serverHTTPNTLMPassword = "Server HTTP NTLM password";
+  /** Keystore for LAPI */
+  public final static String serverHTTPSKeystore = "Server HTTPS truststore";
+  
+  
+  // These parameters are for the LiveLink Authority
   /** User name mapping description.  This replaces username regexp and username spec below. */
   public final static String userNameMapping = "Livelink user name map";
   /** Cache time in seconds */

Modified: manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java?rev=1459947&r1=1459946&r2=1459947&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java (original)
+++ manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java Fri Mar 22 19:34:17 2013
@@ -45,12 +45,19 @@ public class LivelinkAuthority extends o
 {
   public static final String _rcsid = "@(#)$Id: LivelinkAuthority.java 988245 2010-08-23 18:39:35Z kwright $";
 
+  boolean hasConnected = false;
+  
   // Data from the parameters
+  private String serverProtocol = null;
   private String serverName = null;
-  private String serverPortString = null;
   private int serverPort = -1;
   private String serverUsername = null;
   private String serverPassword = null;
+  private String serverHTTPCgi = null;
+  private String serverHTTPNTLMDomain = null;
+  private String serverHTTPNTLMUsername = null;
+  private String serverHTTPNTLMPassword = null;
+  private IKeystoreManager serverHTTPSKeystore = null;
 
   // Data required for maintaining livelink connection
   private LAPI_USERS LLUsers = null;
@@ -113,65 +120,93 @@ public class LivelinkAuthority extends o
   public void connect(ConfigParams configParams)
   {
     super.connect(configParams);
-
-    // First, create server object (llServer)
-    serverName = configParams.getParameter(LiveLinkParameters.serverName);
-    serverPortString = configParams.getParameter(LiveLinkParameters.serverPort);
-    serverUsername = configParams.getParameter(LiveLinkParameters.serverUsername);
-    serverPassword = configParams.getObfuscatedParameter(LiveLinkParameters.serverPassword);
-
-    // These have been deprecated
-    String userNamePattern = configParams.getParameter(LiveLinkParameters.userNameRegexp);
-    String userEvalExpression = configParams.getParameter(LiveLinkParameters.livelinkNameSpec);
-    String userNameMapping = configParams.getParameter(LiveLinkParameters.userNameMapping);
-    if ((userNameMapping == null || userNameMapping.length() == 0) && userNamePattern != null && userEvalExpression != null)
-    {
-      // Create a matchmap using the old system
-      matchMap = new MatchMap();
-      matchMap.appendOldstyleMatchPair(userNamePattern,userEvalExpression);
-    }
-    else
-    {
-      if (userNameMapping == null)
-        userNameMapping = "(.*)\\\\@([A-Z|a-z|0-9|_|-]*)\\\\.(.*)=$(2)\\$(1l)";
-      matchMap = new MatchMap(userNameMapping);
-    }
-
-    if (serverPortString == null)
-      serverPort = 80;
-    else
-      serverPort = new Integer(serverPortString).intValue();
-
-    cacheLifetime = configParams.getParameter(LiveLinkParameters.cacheLifetime);
-    if (cacheLifetime == null)
-      cacheLifetime = "1";
-    cacheLRUsize = configParams.getParameter(LiveLinkParameters.cacheLRUSize);
-    if (cacheLRUsize == null)
-      cacheLRUsize = "1000";    
-
-
   }
 
-  protected void attemptToConnect()
+  protected void getSession()
     throws ManifoldCFException, ServiceInterruption
   {
-    try
-    {
-      responseLifetime = Long.parseLong(this.cacheLifetime) * 60L * 1000L;
-      LRUsize = Integer.parseInt(this.cacheLRUsize);
-    }
-    catch (NumberFormatException e)
+    if (!hasConnected)
     {
-      throw new ManifoldCFException("Cache lifetime or Cache LRU size must be an integer: "+e.getMessage(),e);
-    }
+      // Server parameters
+      serverProtocol = params.getParameter(LiveLinkParameters.serverProtocol);
+      serverName = params.getParameter(LiveLinkParameters.serverName);
+      String serverPortString = params.getParameter(LiveLinkParameters.serverPort);
+      serverUsername = params.getParameter(LiveLinkParameters.serverUsername);
+      serverPassword = params.getObfuscatedParameter(LiveLinkParameters.serverPassword);
+      serverHTTPCgi = params.getParameter(LiveLinkParameters.serverHTTPCgiPath);
+      serverHTTPNTLMDomain = params.getParameter(LiveLinkParameters.serverHTTPNTLMDomain);
+      serverHTTPNTLMUsername = params.getParameter(LiveLinkParameters.serverHTTPNTLMUsername);
+      serverHTTPNTLMPassword = params.getObfuscatedParameter(LiveLinkParameters.serverHTTPNTLMPassword);
+
+      // These have been deprecated
+      String userNamePattern = params.getParameter(LiveLinkParameters.userNameRegexp);
+      String userEvalExpression = params.getParameter(LiveLinkParameters.livelinkNameSpec);
+      String userNameMapping = params.getParameter(LiveLinkParameters.userNameMapping);
+      if ((userNameMapping == null || userNameMapping.length() == 0) && userNamePattern != null && userEvalExpression != null)
+      {
+        // Create a matchmap using the old system
+        matchMap = new MatchMap();
+        matchMap.appendOldstyleMatchPair(userNamePattern,userEvalExpression);
+      }
+      else
+      {
+        if (userNameMapping == null)
+          userNameMapping = "(.*)\\\\@([A-Z|a-z|0-9|_|-]*)\\\\.(.*)=$(2)\\$(1l)";
+        matchMap = new MatchMap(userNameMapping);
+      }
 
-    if (LLUsers == null)
-    {
+      // Server parameter processing
+
+      if (serverProtocol == null || serverProtocol.length() == 0)
+        serverProtocol = "internal";
+        
+      if (serverPortString == null)
+        serverPort = 2099;
+      else
+        serverPort = new Integer(serverPortString).intValue();
+        
+      if (serverHTTPNTLMDomain != null && serverHTTPNTLMDomain.length() == 0)
+        serverHTTPNTLMDomain = null;
+      if (serverHTTPNTLMUsername == null || serverHTTPNTLMUsername.length() == 0)
+      {
+        serverHTTPNTLMUsername = serverUsername;
+        if (serverHTTPNTLMPassword == null || serverHTTPNTLMPassword.length() == 0)
+          serverHTTPNTLMPassword = serverPassword;
+      }
+      else
+      {
+        if (serverHTTPNTLMUsername == null)
+          serverHTTPNTLMUsername = "";
+        if (serverHTTPNTLMPassword == null)
+          serverHTTPNTLMPassword = "";
+      }
+
+      // Set up server ssl if indicated
+      String serverHTTPSKeystoreData = params.getParameter(LiveLinkParameters.serverHTTPSKeystore);
+      if (serverHTTPSKeystoreData != null)
+        serverHTTPSKeystore = KeystoreManagerFactory.make("",serverHTTPSKeystoreData);
+
+      cacheLifetime = params.getParameter(LiveLinkParameters.cacheLifetime);
+      if (cacheLifetime == null)
+        cacheLifetime = "1";
+      cacheLRUsize = params.getParameter(LiveLinkParameters.cacheLRUSize);
+      if (cacheLRUsize == null)
+        cacheLRUsize = "1000";
+      
+      try
+      {
+        responseLifetime = Long.parseLong(this.cacheLifetime) * 60L * 1000L;
+        LRUsize = Integer.parseInt(this.cacheLRUsize);
+      }
+      catch (NumberFormatException e)
+      {
+        throw new ManifoldCFException("Cache lifetime or Cache LRU size must be an integer: "+e.getMessage(),e);
+      }
 
       if (Logging.authorityConnectors.isDebugEnabled())
       {
         String passwordExists = (serverPassword!=null && serverPassword.length() > 0)?"password exists":"";
-        Logging.authorityConnectors.debug("Livelink: Livelink connection parameters: Server='"+serverName+"'; port='"+serverPortString+"'; user name='"+serverUsername+"'; "+passwordExists);
+        Logging.authorityConnectors.debug("Livelink: Livelink connection parameters: Server='"+serverName+"'; port='"+serverPort+"'; user name='"+serverUsername+"'; "+passwordExists);
       }
 
       int sanityRetryCount = FAILURE_RETRY_COUNT;
@@ -179,13 +214,18 @@ public class LivelinkAuthority extends o
       {
         try
         {
-          llServer = new LLSERVER(serverName,serverPort,serverUsername,serverPassword);
+          // Create the session
+          llServer = new LLSERVER(!serverProtocol.equals("internal"),serverProtocol.equals("https"),
+            serverName,serverPort,serverUsername,serverPassword,
+            serverHTTPCgi,serverHTTPNTLMDomain,serverHTTPNTLMUsername,serverHTTPNTLMPassword,
+            serverHTTPSKeystore);
 
           LLUsers = new LAPI_USERS(llServer.getLLSession());
           if (Logging.authorityConnectors.isDebugEnabled())
           {
             Logging.authorityConnectors.debug("Livelink: Livelink session created.");
           }
+          hasConnected = true;
           return;
         }
         catch (RuntimeException e)
@@ -208,8 +248,8 @@ public class LivelinkAuthority extends o
     try
     {
       // Reestablish the session
-      LLUsers = null;
-      attemptToConnect();
+      hasConnected = false;
+      getSession();
       // Get user info for the crawl user, to make sure it works
       int sanityRetryCount = FAILURE_RETRY_COUNT;
       while (true)
@@ -251,15 +291,23 @@ public class LivelinkAuthority extends o
     }
     LLUsers = null;
     matchMap = null;
+    
+    serverProtocol = null;
     serverName = null;
-    serverPortString = null;
     serverPort = -1;
     serverUsername = null;
     serverPassword = null;
-    
+    serverHTTPCgi = null;
+    serverHTTPNTLMDomain = null;
+    serverHTTPNTLMUsername = null;
+    serverHTTPNTLMPassword = null;
+    serverHTTPSKeystore = null;
+
     cacheLifetime = null;
     cacheLRUsize = null;
 
+    hasConnected = false;
+    
     super.disconnect();
   }
 
@@ -327,7 +375,7 @@ public class LivelinkAuthority extends o
 
     try
     {
-      attemptToConnect();
+      getSession();
 
       int sanityRetryCount = FAILURE_RETRY_COUNT;
       while (true)
@@ -489,17 +537,38 @@ public class LivelinkAuthority extends o
     out.print(
 "<script type=\"text/javascript\">\n"+
 "<!--\n"+
+"function ServerDeleteCertificate(aliasName)\n"+
+"{\n"+
+"  editconnection.serverkeystorealias.value = aliasName;\n"+
+"  editconnection.serverconfigop.value = \"Delete\";\n"+
+"  postForm();\n"+
+"}\n"+
+"\n"+
+"function ServerAddCertificate()\n"+
+"{\n"+
+"  if (editconnection.servercertificate.value == \"\")\n"+
+"  {\n"+
+"    alert(\""+Messages.getBodyJavascriptString(locale,"LivelinkConnector.ChooseACertificateFile")+"\");\n"+
+"    editconnection.servercertificate.focus();\n"+
+"  }\n"+
+"  else\n"+
+"  {\n"+
+"    editconnection.serverconfigop.value = \"Add\";\n"+
+"    postForm();\n"+
+"  }\n"+
+"}\n"+
+"\n"+
 "function checkConfig()\n"+
 "{\n"+
 "  if (editconnection.serverport.value != \"\" && !isInteger(editconnection.serverport.value))\n"+
 "  {\n"+
-"    alert(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.AValidNumberIsRequired") + ");\n"+
+"    alert(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.AValidNumberIsRequired") + "\");\n"+
 "    editconnection.serverport.focus();\n"+
 "    return false;\n"+
 "  }\n"+
 "  if (editconnection.usernameregexp.value != \"\" && !isRegularExpression(editconnection.usernameregexp.value))\n"+
 "  {\n"+
-"    alert(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserNameRegularExpressionMustBeValidRegularExpression") + ");\n"+
+"    alert(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserNameRegularExpressionMustBeValidRegularExpression") + "\");\n"+
 "    editconnection.usernameregexp.focus();\n"+
 "    return false;\n"+
 "  }\n"+
@@ -510,22 +579,22 @@ public class LivelinkAuthority extends o
 "{\n"+
 "  if (editconnection.servername.value == \"\")\n"+
 "  {\n"+
-"    alert(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.EnterALivelinkServerName") + ");\n"+
-"    SelectTab(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.Server") + ");\n"+
+"    alert(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.EnterALivelinkServerName") + "\");\n"+
+"    SelectTab(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.Server") + "\");\n"+
 "    editconnection.servername.focus();\n"+
 "    return false;\n"+
 "  }\n"+
 "  if (editconnection.serverport.value == \"\")\n"+
 "  {\n"+
-"    alert(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.AServerPortNumberIsRequired") + ");\n"+
-"    SelectTab(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.Server") + ");\n"+
+"    alert(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.AServerPortNumberIsRequired") + "\");\n"+
+"    SelectTab(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.Server") + "\");\n"+
 "    editconnection.serverport.focus();\n"+
 "    return false;\n"+
 "  }\n"+
 "  if (editconnection.usernameregexp.value == \"\")\n"+
 "  {\n"+
-"    alert(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserNameRegularExpressionCannotBeNull") + ");\n"+
-"    SelectTab(" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserMapping") + ");\n"+
+"    alert(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserNameRegularExpressionCannotBeNull") + "\");\n"+
+"    SelectTab(\"" + Messages.getBodyJavascriptString(locale,"LivelinkConnector.UserMapping") + "\");\n"+
 "    editconnection.usernameregexp.focus();\n"+
 "    return false;\n"+
 "  }\n"+
@@ -579,73 +648,178 @@ public class LivelinkAuthority extends o
     Locale locale, ConfigParams parameters, String tabName)
     throws ManifoldCFException, IOException
   {
-    String serverName = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverName);
+    // LAPI parameters
+    String serverProtocol = parameters.getParameter(LiveLinkParameters.serverProtocol);
+    if (serverProtocol == null)
+      serverProtocol = "internal";
+    String serverName = parameters.getParameter(LiveLinkParameters.serverName);
     if (serverName == null)
       serverName = "localhost";
-    
-    String serverPort = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverPort);
+    String serverPort = parameters.getParameter(LiveLinkParameters.serverPort);
     if (serverPort == null)
       serverPort = "2099";
-    
-    String serverUserName = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverUsername);
+    String serverUserName = parameters.getParameter(LiveLinkParameters.serverUsername);
     if (serverUserName == null)
       serverUserName = "";
-    
-    String serverPassword = parameters.getObfuscatedParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverPassword);
+    String serverPassword = parameters.getObfuscatedParameter(LiveLinkParameters.serverPassword);
     if (serverPassword == null)
       serverPassword = "";
-    
+    String serverHTTPCgiPath = parameters.getParameter(LiveLinkParameters.serverHTTPCgiPath);
+    if (serverHTTPCgiPath == null)
+      serverHTTPCgiPath = "/livelink/livelink.exe";
+    String serverHTTPNTLMDomain = parameters.getParameter(LiveLinkParameters.serverHTTPNTLMDomain);
+    if (serverHTTPNTLMDomain == null)
+      serverHTTPNTLMDomain = "";
+    String serverHTTPNTLMUserName = parameters.getParameter(LiveLinkParameters.serverHTTPNTLMUsername);
+    if (serverHTTPNTLMUserName == null)
+      serverHTTPNTLMUserName = "";
+    String serverHTTPNTLMPassword = parameters.getObfuscatedParameter(LiveLinkParameters.serverHTTPNTLMPassword);
+    if (serverHTTPNTLMPassword == null)
+      serverHTTPNTLMPassword = "";
+    String serverHTTPSKeystore = parameters.getParameter(LiveLinkParameters.serverHTTPSKeystore);
+    IKeystoreManager localServerHTTPSKeystore;
+    if (serverHTTPSKeystore == null)
+      localServerHTTPSKeystore = KeystoreManagerFactory.make("");
+    else
+      localServerHTTPSKeystore = KeystoreManagerFactory.make("",serverHTTPSKeystore);
+
+    // Cache parameters
     String cacheLifetime = parameters.getParameter(LiveLinkParameters.cacheLifetime);
     if (cacheLifetime == null)
       cacheLifetime = "1";
-    
     String cacheLRUsize = parameters.getParameter(LiveLinkParameters.cacheLRUSize);
     if (cacheLRUsize == null)
       cacheLRUsize = "1000";    
 
-    org.apache.manifoldcf.crawler.connectors.livelink.MatchMap matchMap = null;
-    String usernameRegexp = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.userNameRegexp);
-    String livelinkUserExpr = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.livelinkNameSpec);
+    MatchMap matchMap = null;
+    String usernameRegexp = parameters.getParameter(LiveLinkParameters.userNameRegexp);
+    String livelinkUserExpr = parameters.getParameter(LiveLinkParameters.livelinkNameSpec);
     if (usernameRegexp != null && usernameRegexp.length() > 0 && livelinkUserExpr != null)
     {
       // Old-style configuration.  Convert to the new.
-      matchMap = new org.apache.manifoldcf.crawler.connectors.livelink.MatchMap();
+      matchMap = new MatchMap();
       matchMap.appendOldstyleMatchPair(usernameRegexp,livelinkUserExpr);
     }
     else
     {
       // New style configuration.
-      String userNameMapping = parameters.getParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.userNameMapping);
+      String userNameMapping = parameters.getParameter(LiveLinkParameters.userNameMapping);
       if (userNameMapping == null)
         userNameMapping = "^(.*)\\\\@([A-Z|a-z|0-9|_|-]*)\\\\.(.*)$=$(2)\\\\$(1l)";
-      matchMap = new org.apache.manifoldcf.crawler.connectors.livelink.MatchMap(userNameMapping);
+      matchMap = new MatchMap(userNameMapping);
     }
 
     usernameRegexp = matchMap.getMatchString(0);
     livelinkUserExpr = matchMap.getReplaceString(0);
 
     // The "Server" tab
+    // Always pass the whole keystore as a hidden.
+    out.print(
+"<input name=\"serverconfigop\" type=\"hidden\" value=\"Continue\"/>\n"
+    );
+    if (serverHTTPSKeystore != null)
+    {
+      out.print(
+"<input type=\"hidden\" name=\"serverhttpskeystoredata\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPSKeystore)+"\"/>\n"
+      );
+    }
     if (tabName.equals(Messages.getString(locale,"LivelinkConnector.Server")))
     {
       out.print(
 "<table class=\"displaytable\">\n"+
 "  <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
 "  <tr>\n"+
-"    <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"LivelinkConnector.ServerName") + "</nobr></td>\n"+
+"    <td class=\"description\">"+Messages.getBodyString(locale,"LivelinkConnector.ServerProtocol")+"</td>\n"+
+"    <td class=\"value\">\n"+
+"      <select name=\"serverprotocol\" size=\"2\">\n"+
+"        <option value=\"internal\" "+((serverProtocol.equals("internal"))?"selected=\"selected\"":"")+">"+Messages.getBodyString(locale,"LivelinkConnector.internal")+"</option>\n"+
+"        <option value=\"http\" "+((serverProtocol.equals("http"))?"selected=\"selected\"":"")+">http</option>\n"+
+"        <option value=\"https\" "+((serverProtocol.equals("https"))?"selected=\"selected\"":"")+">https</option>\n"+
+"      </select>\n"+
+"    </td>\n"+
+"  </tr>\n"+
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerName")+"</nobr></td>\n"+
 "    <td class=\"value\"><input type=\"text\" size=\"64\" name=\"servername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverName)+"\"/></td>\n"+
 "  </tr>\n"+
 "  <tr>\n"+
-"    <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"LivelinkConnector.ServerPort") + "</nobr></td>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerPort")+"</nobr></td>\n"+
 "    <td class=\"value\"><input type=\"text\" size=\"5\" name=\"serverport\" value=\""+serverPort+"\"/></td>\n"+
 "  </tr>\n"+
 "  <tr>\n"+
-"    <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"LivelinkConnector.ServerUserName") + "</nobr></td>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerUserName")+"</nobr></td>\n"+
 "    <td class=\"value\"><input type=\"text\" size=\"32\" name=\"serverusername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverUserName)+"\"/></td>\n"+
 "  </tr>\n"+
 "  <tr>\n"+
-"    <td class=\"description\"><nobr>" + Messages.getBodyString(locale,"LivelinkConnector.ServerPassword") + "</nobr></td>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerPassword")+"</nobr></td>\n"+
 "    <td class=\"value\"><input type=\"password\" size=\"32\" name=\"serverpassword\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverPassword)+"\"/></td>\n"+
 "  </tr>\n"+
+"  <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerHTTPCGIPath")+"</nobr></td>\n"+
+"    <td class=\"value\"><input type=\"text\" size=\"32\" name=\"serverhttpcgipath\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPCgiPath)+"\"/></td>\n"+
+"  </tr>\n"+
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerHTTPNTLMDomain")+"</nobr><br/><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.SetIfNTLMAuthDesired")+"</nobr></td>\n"+
+"    <td class=\"value\">\n"+
+"      <input type=\"text\" size=\"32\" name=\"serverhttpntlmdomain\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMDomain)+"\"/>\n"+
+"    </td>\n"+
+"  </tr>\n"+
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerHTTPNTLMUserName")+"</nobr><br/><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.SetIfDifferentFromServerUserName")+"</nobr></td>\n"+
+"    <td class=\"value\">\n"+
+"      <input type=\"text\" size=\"32\" name=\"serverhttpntlmusername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMUserName)+"\"/>\n"+
+"    </td>\n"+
+"  </tr>\n"+
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerHTTPNTLMPassword")+"</nobr><br/><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.SetIfDifferentFromServerPassword")+"</nobr></td>\n"+
+"    <td class=\"value\">\n"+
+"      <input type=\"password\" size=\"32\" name=\"serverhttpntlmpassword\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMPassword)+"\"/>\n"+
+"    </td>\n"+
+"  </tr>\n"+
+"  <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"
+      );
+      out.print(
+"  <tr>\n"+
+"    <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.ServerSSLCertificateList")+"</nobr></td>\n"+
+"    <td class=\"value\">\n"+
+"      <input type=\"hidden\" name=\"serverkeystorealias\" value=\"\"/>\n"+
+"      <table class=\"displaytable\">\n"
+      );
+      // List the individual certificates in the store, with a delete button for each
+      String[] contents = localServerHTTPSKeystore.getContents();
+      if (contents.length == 0)
+      {
+        out.print(
+"        <tr><td class=\"message\" colspan=\"2\"><nobr>"+Messages.getBodyString(locale,"LivelinkConnector.NoCertificatesPresent")+"</nobr></td></tr>\n"
+        );
+      }
+      else
+      {
+        int i = 0;
+        while (i < contents.length)
+        {
+          String alias = contents[i];
+          String description = localServerHTTPSKeystore.getDescription(alias);
+          if (description.length() > 128)
+            description = description.substring(0,125) + "...";
+          out.print(
+"        <tr>\n"+
+"          <td class=\"value\"><input type=\"button\" onclick='Javascript:ServerDeleteCertificate(\""+org.apache.manifoldcf.ui.util.Encoder.attributeJavascriptEscape(alias)+"\")' alt=\""+Messages.getAttributeString(locale,"LivelinkConnector.DeleteCert")+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(alias)+"\" value=\""+Messages.getAttributeString(locale,"LivelinkConnector.Delete")+"\"/></td>\n"+
+"          <td>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(description)+"</td>\n"+
+"        </tr>\n"
+          );
+          i++;
+        }
+      }
+      out.print(
+"      </table>\n"+
+"      <input type=\"button\" onclick='Javascript:ServerAddCertificate()' alt=\""+Messages.getAttributeString(locale,"LivelinkConnector.AddCert")+"\" value=\""+Messages.getAttributeString(locale,"LivelinkConnector.Add")+"\"/>&nbsp;\n"+
+"      "+Messages.getBodyString(locale,"LivelinkConnector.Certificate")+"<input name=\"servercertificate\" size=\"50\" type=\"file\"/>\n"+
+"    </td>\n"+
+"  </tr>\n"
+      );
+      out.print(
 "</table>\n"
       );
     }
@@ -653,10 +827,15 @@ public class LivelinkAuthority extends o
     {
       // Hiddens for Server tab
       out.print(
+"<input type=\"hidden\" name=\"serverprotocol\" value=\""+serverProtocol+"\"/>\n"+
 "<input type=\"hidden\" name=\"servername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverName)+"\"/>\n"+
 "<input type=\"hidden\" name=\"serverport\" value=\""+serverPort+"\"/>\n"+
 "<input type=\"hidden\" name=\"serverusername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverUserName)+"\"/>\n"+
-"<input type=\"hidden\" name=\"serverpassword\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverPassword)+"\"/>\n"
+"<input type=\"hidden\" name=\"serverpassword\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverPassword)+"\"/>\n"+
+"<input type=\"hidden\" name=\"serverhttpcgipath\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPCgiPath)+"\"/>\n"+
+"<input type=\"hidden\" name=\"serverhttpntlmdomain\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMDomain)+"\"/>\n"+
+"<input type=\"hidden\" name=\"serverhttpntlmusername\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMUserName)+"\"/>\n"+
+"<input type=\"hidden\" name=\"serverhttpntlmpassword\" value=\""+org.apache.manifoldcf.ui.util.Encoder.attributeEscape(serverHTTPNTLMPassword)+"\"/>\n"
       );
     }
 
@@ -728,34 +907,110 @@ public class LivelinkAuthority extends o
     Locale locale, ConfigParams parameters)
     throws ManifoldCFException
   {
+    // Server parameters
+    String serverProtocol = variableContext.getParameter("serverprotocol");
+    if (serverProtocol != null)
+      parameters.setParameter(LiveLinkParameters.serverProtocol,serverProtocol);
     String serverName = variableContext.getParameter("servername");
     if (serverName != null)
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverName,serverName);
+      parameters.setParameter(LiveLinkParameters.serverName,serverName);
     String serverPort = variableContext.getParameter("serverport");
     if (serverPort != null)
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverPort,serverPort);
+      parameters.setParameter(LiveLinkParameters.serverPort,serverPort);
     String serverUserName = variableContext.getParameter("serverusername");
     if (serverUserName != null)
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverUsername,serverUserName);
+      parameters.setParameter(LiveLinkParameters.serverUsername,serverUserName);
     String serverPassword = variableContext.getParameter("serverpassword");
     if (serverPassword != null)
-      parameters.setObfuscatedParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.serverPassword,serverPassword);
+      parameters.setObfuscatedParameter(LiveLinkParameters.serverPassword,serverPassword);
+    String serverHTTPCgiPath = variableContext.getParameter("serverhttpcgipath");
+    if (serverHTTPCgiPath != null)
+      parameters.setParameter(LiveLinkParameters.serverHTTPCgiPath,serverHTTPCgiPath);
+    String serverHTTPNTLMDomain = variableContext.getParameter("serverhttpntlmdomain");
+    if (serverHTTPNTLMDomain != null)
+      parameters.setParameter(LiveLinkParameters.serverHTTPNTLMDomain,serverHTTPNTLMDomain);
+    String serverHTTPNTLMUserName = variableContext.getParameter("serverhttpntlmusername");
+    if (serverHTTPNTLMUserName != null)
+      parameters.setParameter(LiveLinkParameters.serverHTTPNTLMUsername,serverHTTPNTLMUserName);
+    String serverHTTPNTLMPassword = variableContext.getParameter("serverhttpntlmpassword");
+    if (serverHTTPNTLMPassword != null)
+      parameters.setObfuscatedParameter(LiveLinkParameters.serverHTTPNTLMPassword,serverHTTPNTLMPassword);
+    String serverHTTPSKeystoreValue = variableContext.getParameter("serverhttpskeystoredata");
+    if (serverHTTPSKeystoreValue != null)
+      parameters.setParameter(LiveLinkParameters.serverHTTPSKeystore,serverHTTPSKeystoreValue);
+
+    String serverConfigOp = variableContext.getParameter("serverconfigop");
+    if (serverConfigOp != null)
+    {
+      if (serverConfigOp.equals("Delete"))
+      {
+        String alias = variableContext.getParameter("serverkeystorealias");
+        serverHTTPSKeystoreValue = parameters.getParameter(LiveLinkParameters.serverHTTPSKeystore);
+        IKeystoreManager mgr;
+        if (serverHTTPSKeystoreValue != null)
+          mgr = KeystoreManagerFactory.make("",serverHTTPSKeystoreValue);
+        else
+          mgr = KeystoreManagerFactory.make("");
+        mgr.remove(alias);
+        parameters.setParameter(LiveLinkParameters.serverHTTPSKeystore,mgr.getString());
+      }
+      else if (serverConfigOp.equals("Add"))
+      {
+        String alias = IDFactory.make(threadContext);
+        byte[] certificateValue = variableContext.getBinaryBytes("servercertificate");
+        serverHTTPSKeystoreValue = parameters.getParameter(LiveLinkParameters.serverHTTPSKeystore);
+        IKeystoreManager mgr;
+        if (serverHTTPSKeystoreValue != null)
+          mgr = KeystoreManagerFactory.make("",serverHTTPSKeystoreValue);
+        else
+          mgr = KeystoreManagerFactory.make("");
+        java.io.InputStream is = new java.io.ByteArrayInputStream(certificateValue);
+        String certError = null;
+        try
+        {
+          mgr.importCertificate(alias,is);
+        }
+        catch (Throwable e)
+        {
+          certError = e.getMessage();
+        }
+        finally
+        {
+          try
+          {
+            is.close();
+          }
+          catch (IOException e)
+          {
+            // Eat this exception
+          }
+        }
+
+        if (certError != null)
+        {
+          return "Illegal certificate: "+certError;
+        }
+        parameters.setParameter(LiveLinkParameters.serverHTTPSKeystore,mgr.getString());
+      }
+    }
+
+    // User name parameters
     String usernameRegexp = variableContext.getParameter("usernameregexp");
     String livelinkUserExpr = variableContext.getParameter("livelinkuserexpr");
     if (usernameRegexp != null && livelinkUserExpr != null)
     {
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.userNameRegexp,null);
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.livelinkNameSpec,null);
+      parameters.setParameter(LiveLinkParameters.userNameRegexp,null);
+      parameters.setParameter(LiveLinkParameters.livelinkNameSpec,null);
 
-      org.apache.manifoldcf.crawler.connectors.livelink.MatchMap matchMap = new org.apache.manifoldcf.crawler.connectors.livelink.MatchMap();
+      MatchMap matchMap = new MatchMap();
       matchMap.appendMatchPair(usernameRegexp,livelinkUserExpr);
-      parameters.setParameter(org.apache.manifoldcf.crawler.connectors.livelink.LiveLinkParameters.userNameMapping,matchMap.toString());
+      parameters.setParameter(LiveLinkParameters.userNameMapping,matchMap.toString());
     }
 
+    // Cache parameters
     String cacheLifetime = variableContext.getParameter("cachelifetime");
     if (cacheLifetime != null)
       parameters.setParameter(LiveLinkParameters.cacheLifetime,cacheLifetime);
-
     String cacheLRUsize = variableContext.getParameter("cachelrusize");
     if (cacheLRUsize != null)
       parameters.setParameter(LiveLinkParameters.cacheLRUSize,cacheLRUsize);
@@ -792,7 +1047,8 @@ public class LivelinkAuthority extends o
 "      <nobr>"+org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param)+"=********</nobr><br/>\n"
         );
       }
-      else if (param.length() >="keystore".length() && param.substring(param.length()-"keystore".length()).equalsIgnoreCase("keystore"))
+      else if (param.length() >="keystore".length() && param.substring(param.length()-"keystore".length()).equalsIgnoreCase("keystore") ||
+        param.length() > "truststore".length() && param.substring(param.length()-"truststore".length()).equalsIgnoreCase("truststore"))
       {
         IKeystoreManager kmanager = KeystoreManagerFactory.make("",value);
         out.print(
@@ -849,6 +1105,11 @@ public class LivelinkAuthority extends o
       String details = llServer.getErrors();
       throw new ManifoldCFException("Livelink API error: "+e.getMessage()+((details==null)?"":"; "+details),e);
     }
+    else if (e instanceof com.opentext.api.LLSSLNotAvailableException)
+    {
+      String details = llServer.getErrors();
+      throw new ManifoldCFException("Missing llssl.jar error: "+e.getMessage()+((details==null)?"":"; "+details),e);
+    }
     else if (e instanceof com.opentext.api.LLIllegalOperationException)
     {
       // This usually means that LAPI has had a minor communication difficulty but hasn't reported it accurately.
@@ -856,8 +1117,11 @@ public class LivelinkAuthority extends o
       String details = llServer.getErrors();
       return assessRetry(sanityRetryCount,new ManifoldCFException("Livelink API illegal operation error: "+e.getMessage()+((details==null)?"":"; "+details),e));
     }
-    else if (e instanceof com.opentext.api.LLIOException)
+    else if (e instanceof com.opentext.api.LLIOException || (e instanceof RuntimeException && e.getClass().getName().startsWith("com.opentext.api.")))
     {
+      // Catching obfuscated and unspecified opentext runtime exceptions now too - these come from llssl.jar.  We
+      // have to presume these are SSL connection errors; nothing else to go by unfortunately.  UGH.
+
       // LAPI is returning errors that are not terribly explicit, and I don't have control over their wording, so check that server can be resolved by DNS,
       // so that a better error message can be returned.
       try