Add Curl Logging to Exchnage Sync Adapter

Change-Id: I09e59440b79d392d7bd85e17f8641eeba4a2c17a
diff --git a/src/com/android/exchange/service/EasServerConnection.java b/src/com/android/exchange/service/EasServerConnection.java
index 1f07854..c6577c1 100644
--- a/src/com/android/exchange/service/EasServerConnection.java
+++ b/src/com/android/exchange/service/EasServerConnection.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Base64;
+import android.util.Log;
 
 import com.android.emailcommon.internet.MimeUtility;
 import com.android.emailcommon.provider.Account;
@@ -40,17 +41,25 @@
 import com.android.exchange.eas.EasConnectionCache;
 import com.android.mail.utils.LogUtils;
 
+import org.apache.http.Header;
 import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpOptions;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.RequestWrapper;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URI;
 import java.security.cert.CertificateException;
@@ -173,7 +182,13 @@
             HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
             HttpConnectionParams.setSoTimeout(params, (int)(timeout));
             HttpConnectionParams.setSocketBufferSize(params, 8192);
-            mClient = new DefaultHttpClient(getClientConnectionManager(), params);
+            mClient = new DefaultHttpClient(getClientConnectionManager(), params) {
+                protected BasicHttpProcessor createHttpProcessor() {
+                    final BasicHttpProcessor processor = super.createHttpProcessor();
+                    processor.addRequestInterceptor(new CurlLogger());
+                    return processor;
+                }
+            };
         }
         return mClient;
     }
@@ -519,4 +534,85 @@
         ContentResolver.requestSync(amAccount, authority, extras);
     }
 
+    // Curl Logging is copied over from AndroidHttpClient. Just switching to AndroidHttpClient is
+    // not trivial so it's easier to borrow the curl logging code this way.
+
+    /**
+     * Logs cURL commands equivalent to requests.
+     */
+    private class CurlLogger implements HttpRequestInterceptor {
+        @Override
+        public void process(HttpRequest request, HttpContext context) throws IOException {
+            if (request instanceof HttpUriRequest) {
+                if ((Build.TYPE.equals("userdebug") || Build.TYPE.equals("eng"))
+                        &&  Log.isLoggable(TAG, Log.VERBOSE)) {
+                    // Allow us to log auth token on dev devices - this is not a big security risk
+                    // because dev devices have a readable account.db file where all the auth tokens
+                    // are stored.
+                    Log.d(TAG, toCurl((HttpUriRequest) request, true));
+                } else  if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, toCurl((HttpUriRequest) request, false));
+                }
+            }
+        }
+    }
+
+    /**
+     * Generates a cURL command equivalent to the given request.
+     */
+    private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("curl ");
+
+        for (Header header: request.getAllHeaders()) {
+            builder.append("--header \"");
+            if (!logAuthToken
+                    && (header.getName().equals("Authorization") ||
+                    header.getName().equals("Cookie"))) {
+
+                builder.append(header.getName()).append(": ").append("${token}");
+            } else {
+                builder.append(header.toString().trim());
+            }
+            builder.append("\" ");
+        }
+
+        URI uri = request.getURI();
+
+        // If this is a wrapped request, use the URI from the original
+        // request instead. getURI() on the wrapper seems to return a
+        // relative URI. We want an absolute URI.
+        if (request instanceof RequestWrapper) {
+            HttpRequest original = ((RequestWrapper) request).getOriginal();
+            if (original instanceof HttpUriRequest) {
+                uri = ((HttpUriRequest) original).getURI();
+            }
+        }
+
+        builder.append("\"");
+        builder.append(uri);
+        builder.append("\"");
+
+        if (request instanceof HttpEntityEnclosingRequest) {
+            HttpEntityEnclosingRequest entityRequest =
+                    (HttpEntityEnclosingRequest) request;
+            HttpEntity entity = entityRequest.getEntity();
+            if (entity != null && entity.isRepeatable()) {
+                if (entity.getContentLength() < 1024) {
+                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                    entity.writeTo(stream);
+
+                    String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP);
+                    builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; ");
+                    builder.append(" --data-binary @/tmp/$$.bin");
+                } else {
+                    builder.append(" [TOO MUCH DATA TO INCLUDE]");
+                }
+            }
+        }
+
+        return builder.toString();
+    }
+
 }