Host Name site collection support
Code Review : https://codereview.appspot.com/13752043/
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index e90f18f..d46e0ea 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -368,8 +368,9 @@
     authenticationHandler.start();
     executor = executorFactory.call();
     try {
+      SiteAdaptor vsAdaptor = getSiteAdaptor(virtualServer, virtualServer);
       SiteDataClient virtualServerSiteDataClient =
-          getSiteAdaptor(virtualServer, virtualServer).getSiteDataClient();
+          vsAdaptor.getSiteDataClient();
       rareModCache
           = new RareModificationCache(virtualServerSiteDataClient, executor);
 
@@ -381,6 +382,29 @@
       // Version for SP2010 is 14. Version for SP2013 is 15.
       isSp2007 = (version == null);
       log.log(Level.FINE, "isSP2007 : {0}", isSp2007);
+      
+      // Loop through all host-named site collections and add them to
+      // whitelist for authenticator.
+      for (ContentDatabases.ContentDatabase cdcd : 
+          vs.getContentDatabases().getContentDatabase()) {
+        ContentDatabase cd;
+        try {
+          cd = virtualServerSiteDataClient.getContentContentDatabase(
+              cdcd.getID(), true);
+        } catch (IOException ex) {
+          log.log(Level.WARNING, "Failed to get sites for database {0}",
+              cdcd.getID());
+          continue;
+        }
+        if (cd.getSites() == null) {
+          continue;
+        }
+        for (Sites.Site siteListing : cd.getSites().getSite()) {
+          String siteString
+              = vsAdaptor.encodeDocId(siteListing.getURL()).getUniqueId();
+          ntlmAuthenticator.addPermitForHost(spUrlToUri(siteString).toURL());
+        }
+      }      
     } catch (Exception e) {
       // Don't leak the executor.
       destroy();
@@ -414,13 +438,21 @@
     log.entering("SharePointAdaptor", "getDocContent",
         new Object[] {request, response});
     DocId id = request.getDocId();
-    SiteAdaptor virtualServerSiteAdaptor
-        = getSiteAdaptor(virtualServer, virtualServer);
-    if (id.equals(virtualServerDocId)) {
+     if (id.equals(virtualServerDocId)) {
+      SiteAdaptor virtualServerSiteAdaptor
+          = getSiteAdaptor(virtualServer, virtualServer);
       virtualServerSiteAdaptor.getVirtualServerDocContent(request, response);
     } else {
+      SiteAdaptor rootSiteAdaptor
+          = getRootAdaptorForUrl(spUrlToUri(id.getUniqueId()));
+      if (rootSiteAdaptor == null) {
+        log.log(Level.FINE, "responding not found");
+        response.respondNotFound();
+        log.exiting("SharePointAdaptor", "getDocContent");
+        return;
+      }
       SiteAdaptor siteAdaptor
-          = virtualServerSiteAdaptor.getAdaptorForUrl(id.getUniqueId());
+          = rootSiteAdaptor.getAdaptorForUrl(id.getUniqueId());
       if (siteAdaptor == null) {
         log.log(Level.FINE, "responding not found");
         response.respondNotFound();
@@ -459,6 +491,7 @@
       for (Sites.Site siteListing : cd.getSites().getSite()) {
         String siteString
             = vsAdaptor.encodeDocId(siteListing.getURL()).getUniqueId();
+        ntlmAuthenticator.addPermitForHost(spUrlToUri(siteString).toURL());           
         SiteAdaptor siteAdaptor = getSiteAdaptor(siteString, siteString);
         Site site;
         try {
@@ -637,6 +670,9 @@
         siteUrl = siteUrl.substring(0, siteUrl.length() - 1);
       }
       docIds.add(new DocId(siteUrl));
+      // Add modified site to whitelist for authenticator as this might be new
+      // host name site collection.
+      ntlmAuthenticator.addPermitForHost(spUrlToUri(siteUrl).toURL());
       if ("UpdateSecurity".equals(changes.getChange())) {
         updatedSiteSecurity.add(siteUrl);
       }
@@ -807,6 +843,25 @@
     AbstractAdaptor.main(new SharePointAdaptor(), args);
   }
 
+  private SiteAdaptor getRootAdaptorForUrl(URI uri) throws IOException {
+    if (!ntlmAuthenticator.isPermittedHost(uri.toURL())) {
+      log.log(Level.WARNING, "URL {0} not white listed", uri);
+      return null;
+    }
+    String rootUrl;
+    try {
+       rootUrl = getRootUrl(uri);
+    } catch (URISyntaxException e) {
+      throw new IOException(e);
+    }
+    return getSiteAdaptor(rootUrl, rootUrl);
+  }
+  
+  private String getRootUrl(URI uri) throws URISyntaxException {
+    return new URI(
+        uri.getScheme(), uri.getAuthority(), null, null, null).toString();
+  }
+
   @VisibleForTesting
   class SiteAdaptor {
     private final SiteDataClient siteDataClient;
@@ -2273,6 +2328,11 @@
     public void addPermitForHost(URL urlContainingHost) {
       permittedHosts.add(urlToHostString(urlContainingHost));
     }
+    
+    private boolean isPermittedHost(URL toVerify) {
+      return permittedHosts.contains(urlToHostString(toVerify));
+    }
+    
 
     private String urlToHostString(URL url) {
       // If the port is missing (so that the default is used), we replace it
@@ -2287,7 +2347,7 @@
     @Override
     protected PasswordAuthentication getPasswordAuthentication() {
       URL url = getRequestingURL();
-      if (permittedHosts.contains(urlToHostString(url))) {
+      if (isPermittedHost(url)) {
         return new PasswordAuthentication(username, password);
       } else {
         return super.getPasswordAuthentication();
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index 53e3264..9c21730 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -222,7 +222,8 @@
       = MockSoapFactory.blank()
       .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
       .endpoint(VS_ENDPOINT, MockSiteData.blank()
-          .register(VS_CONTENT_EXCHANGE));
+          .register(VS_CONTENT_EXCHANGE)
+          .register(CD_CONTENT_EXCHANGE));
 
   @Rule
   public ExpectedException thrown = ExpectedException.none();
@@ -352,6 +353,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(new SiteAndWebExchange(
                 "http://wronghost:1/", 1, null, null)));
 
@@ -373,6 +375,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(new SiteAndWebExchange(
                 wrongPage, 0, "http://localhost:1", "http://localhost:1"))
             .register(new URLSegmentsExchange(
@@ -487,6 +490,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(SITES_SITECOLLECTION_SAW_EXCHANGE))
         .endpoint(SITES_SITECOLLECTION_ENDPOINT, MockSiteData.blank()
             .register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
@@ -543,6 +547,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(SITES_SITECOLLECTION_SAW_EXCHANGE))
         .endpoint(SITES_SITECOLLECTION_ENDPOINT, MockSiteData.blank()
             .register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
@@ -587,6 +592,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(SITES_SITECOLLECTION_SAW_EXCHANGE))
         .endpoint(SITES_SITECOLLECTION_ENDPOINT, MockSiteData.blank()
             .register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
@@ -631,6 +637,7 @@
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE)
             .register(SITES_SITECOLLECTION_SAW_EXCHANGE))
         .endpoint(SITES_SITECOLLECTION_ENDPOINT, siteData);
     SiteDataSoap siteDataState1 = MockSiteData.blank()
@@ -1239,7 +1246,8 @@
         + "</ContentDatabase></SPContentDatabase>";
     final ReferenceSiteData siteData = new ReferenceSiteData();
     SiteDataSoap state0 = MockSiteData.blank()
-        .register(VS_CONTENT_EXCHANGE);
+        .register(VS_CONTENT_EXCHANGE)
+        .register(CD_CONTENT_EXCHANGE);
     SiteDataSoap state1 = new UnsupportedSiteData() {
       @Override
       public void getContent(ObjectType objectType, String objectId,
@@ -1348,6 +1356,7 @@
     final AtomicLong atomicNumberGetChangesCalls = new AtomicLong(0);
     final SiteDataSoap siteData = MockSiteData.blank()
         .register(vsContentExchange)
+        .register(CD_CONTENT_EXCHANGE)
         .register(new ContentExchange(ObjectType.CONTENT_DATABASE,
               "{4fb7dea1-2912-4927-9eda-1ea2f0977cf8}", null, null, false,
               false, null, getContentContentDatabase4fb))