1 // Copyright 2013 Google Inc. All Rights Reserved. 2 3 package com.android.exchange.utility; 4 5 import android.os.Build; 6 import android.util.Base64; 7 import android.util.Log; 8 9 import com.android.exchange.Eas; 10 11 import org.apache.http.Header; 12 import org.apache.http.HttpEntity; 13 import org.apache.http.HttpEntityEnclosingRequest; 14 import org.apache.http.HttpRequest; 15 import org.apache.http.HttpRequestInterceptor; 16 import org.apache.http.client.methods.HttpUriRequest; 17 import org.apache.http.impl.client.RequestWrapper; 18 import org.apache.http.protocol.HttpContext; 19 20 import java.io.ByteArrayOutputStream; 21 import java.io.IOException; 22 import java.net.URI; 23 24 /** 25 * Logs cURL commands equivalent to requests. 26 * Curl Logging is copied over from AndroidHttpClient. Just switching to AndroidHttpClient is 27 * not trivial so it's easier to borrow the curl logging code this way. 28 */ 29 public class CurlLogger implements HttpRequestInterceptor { 30 private static final String TAG = Eas.LOG_TAG; 31 32 @Override 33 public void process(HttpRequest request, HttpContext context) throws IOException { 34 if (request instanceof HttpUriRequest) { 35 if ((Build.TYPE.equals("userdebug") || Build.TYPE.equals("eng")) 36 && Log.isLoggable(TAG, Log.VERBOSE)) { 37 // Allow us to log auth token on dev devices - this is not a big security risk 38 // because dev devices have a readable account.db file where all the auth tokens 39 // are stored. 40 Log.d(TAG, toCurl((HttpUriRequest) request, true)); 41 } else if (Log.isLoggable(TAG, Log.DEBUG)) { 42 Log.d(TAG, toCurl((HttpUriRequest) request, false)); 43 } 44 } 45 } 46 47 /** 48 * Generates a cURL command equivalent to the given request. 49 */ 50 private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { 51 StringBuilder builder = new StringBuilder(); 52 53 builder.append("curl "); 54 55 for (Header header: request.getAllHeaders()) { 56 builder.append("--header \""); 57 if (!logAuthToken 58 && (header.getName().equals("Authorization") || 59 header.getName().equals("Cookie"))) { 60 61 builder.append(header.getName()).append(": ").append("${token}"); 62 } else { 63 builder.append(header.toString().trim()); 64 } 65 builder.append("\" "); 66 } 67 68 URI uri = request.getURI(); 69 70 // If this is a wrapped request, use the URI from the original 71 // request instead. getURI() on the wrapper seems to return a 72 // relative URI. We want an absolute URI. 73 if (request instanceof RequestWrapper) { 74 HttpRequest original = ((RequestWrapper) request).getOriginal(); 75 if (original instanceof HttpUriRequest) { 76 uri = ((HttpUriRequest) original).getURI(); 77 } 78 } 79 80 builder.append("\""); 81 builder.append(uri); 82 builder.append("\""); 83 84 if (request instanceof HttpEntityEnclosingRequest) { 85 HttpEntityEnclosingRequest entityRequest = 86 (HttpEntityEnclosingRequest) request; 87 HttpEntity entity = entityRequest.getEntity(); 88 if (entity != null && entity.isRepeatable()) { 89 if (entity.getContentLength() < 1024) { 90 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 91 entity.writeTo(stream); 92 93 String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP); 94 builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; "); 95 builder.append(" --data-binary @/tmp/$$.bin"); 96 } else { 97 builder.append(" [TOO MUCH DATA TO INCLUDE]"); 98 } 99 } 100 } 101 102 return builder.toString(); 103 } 104 105 } 106