1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.exchange.utility;
18
19import android.util.Base64;
20import android.util.Log;
21
22import com.android.exchange.Eas;
23import com.android.mail.utils.LogUtils;
24
25import org.apache.http.Header;
26import org.apache.http.HttpEntity;
27import org.apache.http.entity.BufferedHttpEntity;
28import org.apache.http.HttpResponse;
29import org.apache.http.HttpResponseInterceptor;
30import org.apache.http.protocol.HttpContext;
31
32import java.io.ByteArrayOutputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.util.zip.GZIPInputStream;
36
37/**
38 * Dumps the wbxml in base64 (much like {@link CurlLogger}) so that the
39 * response from Exchange can be viewed for debugging purposes.
40 */
41public class WbxmlResponseLogger implements HttpResponseInterceptor {
42    private static final String TAG = Eas.LOG_TAG;
43    protected static final int MAX_LENGTH = 1024;
44
45    protected static boolean shouldLogResponse(final long contentLength) {
46        // Not going to bother if there is a lot of content since most of that information
47        // will probably just be message contents anyways.
48        return contentLength < MAX_LENGTH;
49    }
50
51    protected static String processContentEncoding(final Header encodingHeader) {
52        if (encodingHeader != null) {
53            final String encodingValue = encodingHeader.getValue();
54            return (encodingValue == null) ? "UTF-8" : encodingValue;
55        }
56        return "UTF-8";
57    }
58
59    protected static byte[] getContentAsByteArray(InputStream is, int batchSize)
60        throws IOException {
61        // Start building our byte array to encode and dump.
62        int count;
63        final byte[] data = new byte[batchSize];
64        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
65        while ((count = is.read(data, 0, data.length)) != -1) {
66            buffer.write(data, 0, count);
67        }
68        buffer.flush();
69        return buffer.toByteArray();
70    }
71
72    @Override
73    public void process(HttpResponse response, HttpContext context) throws IOException {
74        if (Log.isLoggable(TAG, Log.DEBUG)) {
75            // Wrap the HttpEntity so the response InputStream can be requested and processed
76            // numerous times.
77            response.setEntity(new BufferedHttpEntity(response.getEntity()));
78
79            // Now grab the wrapped HttpEntity so that you safely can process the response w/o
80            // affecting the core response processing module.
81            final HttpEntity entity = response.getEntity();
82            if (!shouldLogResponse(entity.getContentLength())) {
83                LogUtils.d(TAG, "wbxml response: [TOO MUCH DATA TO INCLUDE]");
84                return;
85            }
86
87            // We need to figure out the encoding in the case that it is gzip and we need to
88            // inflate it during processing.
89            final Header encodingHeader = entity.getContentEncoding();
90            final String encoding = processContentEncoding(encodingHeader);
91
92            final InputStream is;
93            if (encoding.equals("gzip")) {
94                // We need to inflate this first.
95                final InputStream unwrappedIs = response.getEntity().getContent();
96                is = new GZIPInputStream(unwrappedIs);
97            } else {
98                is = response.getEntity().getContent();
99            }
100
101            final byte currentXMLBytes[] = getContentAsByteArray(is, MAX_LENGTH);
102
103            // Now let's dump out the base 64 encoded bytes and the rest of the command that will
104            // tell us what the response is.
105            final String base64 = Base64.encodeToString(currentXMLBytes, Base64.NO_WRAP);
106            LogUtils.d(TAG, "wbxml response: echo '%s' | base64 -d | wbxml", base64);
107        }
108    }
109
110}
111