Merge branch 'master' of https://code.google.com/p/plexi
diff --git a/resources/com/google/enterprise/adaptor/TranslationStrings.properties b/resources/com/google/enterprise/adaptor/TranslationStrings.properties
index d51f2e7..b2b0208 100644
--- a/resources/com/google/enterprise/adaptor/TranslationStrings.properties
+++ b/resources/com/google/enterprise/adaptor/TranslationStrings.properties
@@ -17,17 +17,25 @@
 # The request could not be decoded.
 HTTP_BAD_REQUEST_ERROR_DECODING=Error 400: Bad Request. Error decoding message.
 # The request did not pass required security rules.
-HTTP_BAD_REQUEST_SECURITY_ERROR=Error 400: Bad Request. Security error while decoding message.
+HTTP_BAD_REQUEST_SECURITY_ERROR=Error 400: Bad Request. Security error while \
+  decoding message.
 # The client is not allowed to access the requested URL.
 HTTP_FORBIDDEN=Error 403: Forbidden
-# The requested URL could not be found.
+# The client is not allowed to access the requested URL since they could not be
+# authenticated.
 HTTP_FORBIDDEN_AUTHN_FAILURE=Error 403: Forbidden. You were not authenticated.
+# The client is not allowed to access the requested URL since it will use the
+# information in an unsupported way.
+HTTP_FORBIDDEN_SECMGR=Error 403: Forbidden. Requests from the GSA's Security \
+  Manager are always denied, since HEAD requests for checking authorization \
+  are not supported.
 HTTP_NOT_FOUND=Error 404: Not Found
 # The HTTP request had an unsupported method for the URL requested. The method
 # in HTTP is typically GET, POST, or HEAD, but others exist as well.
 HTTP_BAD_METHOD=Error 405: Method Not Allowed
 # The request was missing a header, or the expected header was invalid.
-HTTP_CONFLICT_INVALID_HEADER=Error 409: Conflict. You must provide a valid {0} HTTP header.
+HTTP_CONFLICT_INVALID_HEADER=Error 409: Conflict. You must provide a valid {0} \
+  HTTP header.
 # The server encountered an error of some sort while processing the request.
 HTTP_INTERNAL_ERROR=Error 500: Internal Server Error
 # The name of a status that monitors whether the GSA has made a request
diff --git a/src/com/google/enterprise/adaptor/AbstractHandler.java b/src/com/google/enterprise/adaptor/AbstractHandler.java
index c288537..8e480b7 100644
--- a/src/com/google/enterprise/adaptor/AbstractHandler.java
+++ b/src/com/google/enterprise/adaptor/AbstractHandler.java
@@ -14,6 +14,7 @@
 
 package com.google.enterprise.adaptor;
 
+import com.sun.net.httpserver.Headers;
 import com.sun.net.httpserver.HttpExchange;
 import com.sun.net.httpserver.HttpHandler;
 import com.sun.net.httpserver.HttpsServer;
@@ -83,10 +84,9 @@
     this.defaultEncoding = defaultEncoding;
   }
 
-  protected String getLoggableRequestHeaders(HttpExchange ex) {
+  String getLoggableHeaders(Headers headers) {
     StringBuilder sb = new StringBuilder();
-    for (Map.Entry<String, List<String>> me
-         : ex.getRequestHeaders().entrySet()) {
+    for (Map.Entry<String, List<String>> me : headers.entrySet()) {
       for (String value : me.getValue()) {
         sb.append(me.getKey());
         sb.append(": ");
@@ -98,11 +98,19 @@
     return sb.substring(0, sb.length() - 2);
   }
 
-  protected void logRequest(HttpExchange ex) {
+  private void logRequest(HttpExchange ex) {
     if (log.isLoggable(Level.FINER)) {
       log.log(Level.FINER, "Received {1} request to {0}. Headers: '{'{2}'}'",
               new Object[] {ex.getRequestURI(), ex.getRequestMethod(),
-                            getLoggableRequestHeaders(ex)});
+                            getLoggableHeaders(ex.getRequestHeaders())});
+    }
+  }
+
+  private void logResponse(HttpExchange ex) {
+    if (log.isLoggable(Level.FINER)) {
+      log.log(Level.FINER, "Responded to {1} request {0}. Headers: '{'{2}'}'",
+              new Object[] {ex.getRequestURI(), ex.getRequestMethod(),
+                            getLoggableHeaders(ex.getResponseHeaders())});
     }
   }
 
@@ -310,6 +318,7 @@
                       Translation.HTTP_INTERNAL_ERROR);
       }
     } finally {
+      logResponse(ex);
       log.fine("ending");
     }
   }
diff --git a/src/com/google/enterprise/adaptor/DocumentHandler.java b/src/com/google/enterprise/adaptor/DocumentHandler.java
index 7215385..ffb1ee3 100644
--- a/src/com/google/enterprise/adaptor/DocumentHandler.java
+++ b/src/com/google/enterprise/adaptor/DocumentHandler.java
@@ -222,6 +222,14 @@
    * @return {@code true} if user authzed
    */
   private boolean authzed(HttpExchange ex, DocId docId) throws IOException {
+    if ("SecMgr".equals(ex.getRequestHeaders().getFirst("User-Agent"))) {
+      // Assume that the SecMgr is performing a "HEAD" request to check authz.
+      // We don't support this, so we always issue deny.
+      cannedRespond(ex, HttpURLConnection.HTTP_FORBIDDEN,
+                    Translation.HTTP_FORBIDDEN_SECMGR);
+      return false;
+    }
+
     if (requestIsFromFullyTrustedClient(ex)) {
       journal.recordGsaContentRequest(docId);
     } else {
diff --git a/src/com/google/enterprise/adaptor/Translation.java b/src/com/google/enterprise/adaptor/Translation.java
index bcfd53d..92cab76 100644
--- a/src/com/google/enterprise/adaptor/Translation.java
+++ b/src/com/google/enterprise/adaptor/Translation.java
@@ -33,6 +33,7 @@
   HTTP_BAD_REQUEST_SECURITY_ERROR,
   HTTP_FORBIDDEN,
   HTTP_FORBIDDEN_AUTHN_FAILURE,
+  HTTP_FORBIDDEN_SECMGR,
   HTTP_NOT_FOUND,
   HTTP_BAD_METHOD,
   HTTP_CONFLICT_INVALID_HEADER,
diff --git a/test/com/google/enterprise/adaptor/AbstractHandlerTest.java b/test/com/google/enterprise/adaptor/AbstractHandlerTest.java
index fa80b45..d434415 100644
--- a/test/com/google/enterprise/adaptor/AbstractHandlerTest.java
+++ b/test/com/google/enterprise/adaptor/AbstractHandlerTest.java
@@ -40,7 +40,8 @@
   public void testLoggableRequestHeaders() {
     ex.getRequestHeaders().set("a", "1");
     ex.getRequestHeaders().set("B", "2");
-    assertEquals("A: 1, B: 2", handler.getLoggableRequestHeaders(ex));
+    assertEquals("A: 1, B: 2",
+        handler.getLoggableHeaders(ex.getRequestHeaders()));
   }
 
   @Test
diff --git a/test/com/google/enterprise/adaptor/DocumentHandlerTest.java b/test/com/google/enterprise/adaptor/DocumentHandlerTest.java
index a176821..63bad29 100644
--- a/test/com/google/enterprise/adaptor/DocumentHandlerTest.java
+++ b/test/com/google/enterprise/adaptor/DocumentHandlerTest.java
@@ -96,6 +96,13 @@
   }
 
   @Test
+  public void testDenyForSecMgr() throws Exception {
+    ex.getRequestHeaders().add("User-Agent", "SecMgr");
+    handler.handle(ex);
+    assertEquals(403, ex.getResponseCode());
+  }
+
+  @Test
   public void testSecurityDeny() throws Exception {
     DocumentHandler handler = createDefaultHandlerForAdaptor(
         new PrivateMockAdaptor());