1b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee/* 2b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Copyright (C) 2014 The Android Open Source Project 3b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * 4b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Licensed under the Apache License, Version 2.0 (the "License"); 5b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * you may not use this file except in compliance with the License. 6b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * You may obtain a copy of the License at 7b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * 8b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * http://www.apache.org/licenses/LICENSE-2.0 9b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * 10b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Unless required by applicable law or agreed to in writing, software 11b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * distributed under the License is distributed on an "AS IS" BASIS, 12b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * See the License for the specific language governing permissions and 14b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * limitations under the License. 15b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 16b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 17b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leepackage com.android.exchange.eas; 18b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 19b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport android.content.Context; 20b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport android.os.RemoteException; 21b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 22ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdonimport com.android.emailcommon.provider.Account; 23b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.emailcommon.provider.EmailContent; 24b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.emailcommon.provider.EmailContent.Attachment; 25b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.emailcommon.service.EmailServiceStatus; 26b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.emailcommon.service.IEmailServiceCallback; 27b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.emailcommon.utility.AttachmentUtilities; 28b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.Eas; 29b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.EasResponse; 30b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.adapter.ItemOperationsParser; 31b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.adapter.Serializer; 32b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.adapter.Tags; 33b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.service.EasService; 34b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.exchange.utility.UriCodec; 35b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport com.android.mail.utils.LogUtils; 36b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 37b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport org.apache.http.HttpEntity; 38b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 39b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.Closeable; 40b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.File; 41b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.FileInputStream; 42b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.FileNotFoundException; 43b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.FileOutputStream; 44b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.IOException; 45b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.InputStream; 46b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leeimport java.io.OutputStream; 47b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 48b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee/** 49b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * This class performs the heavy lifting of loading attachments from the Exchange server to the 50b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * device in a local file. 51b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * TODO: Add ability to call back to UI when this failed, and generally better handle error cases. 52b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 53b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Leepublic final class EasLoadAttachment extends EasOperation { 54b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 55aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee public static final int RESULT_SUCCESS = 0; 56aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee 57b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** Attachment Loading Errors **/ 58b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static final int RESULT_LOAD_ATTACHMENT_INFO_ERROR = -100; 59b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static final int RESULT_ATTACHMENT_NO_LOCATION_ERROR = -101; 60b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static final int RESULT_ATTACHMENT_LOAD_MESSAGE_ERROR = -102; 61b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static final int RESULT_ATTACHMENT_INTERNAL_HANDLING_ERROR = -103; 62b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static final int RESULT_ATTACHMENT_RESPONSE_PARSING_ERROR = -104; 63b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 64b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private final IEmailServiceCallback mCallback; 65b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private final long mAttachmentId; 66b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 67b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // These members are set in a future point in time outside of the constructor. 68b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private Attachment mAttachment; 69b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 70b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 71b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Constructor for use with {@link EasService} when performing an actual sync. 72b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param context Our {@link Context}. 73ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon * @param account The account we're loading the attachment for. 74b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param attachmentId The local id of the attachment (i.e. its id in the database). 75b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param callback The callback for any status updates. 76b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 77ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon public EasLoadAttachment(final Context context, final Account account, final long attachmentId, 78b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final IEmailServiceCallback callback) { 79b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // The account is loaded before performOperation but it is not guaranteed to be available 80b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // before then. 81ec620ef7363101f5ce8208d17ac496e882d995fdMartin Hibdon super(context, account); 82b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mCallback = callback; 83b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mAttachmentId = attachmentId; 84b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 85b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 86b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 87b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Helper function that makes a callback for us within our implementation. 88b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 89b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private static void doStatusCallback(final IEmailServiceCallback callback, 90b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final long messageKey, final long attachmentId, final int status, final int progress) { 91b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (callback != null) { 92b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 93b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // loadAttachmentStatus is mart of IEmailService interface. 94b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee callback.loadAttachmentStatus(messageKey, attachmentId, status, progress); 95b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (final RemoteException e) { 96b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "RemoteException in loadAttachment: %s", e.getMessage()); 97b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 98b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 99b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 100b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 101b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 102b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Helper class that is passed to other objects to perform callbacks for us. 103b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 104b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public static class ProgressCallback { 105b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private final IEmailServiceCallback mCallback; 106b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private final EmailContent.Attachment mAttachment; 107b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 108b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public ProgressCallback(final IEmailServiceCallback callback, 109b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final EmailContent.Attachment attachment) { 110b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mCallback = callback; 111b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mAttachment = attachment; 112b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 113b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 114b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee public void doCallback(final int progress) { 115b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee doStatusCallback(mCallback, mAttachment.mMessageKey, mAttachment.mId, 116b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee EmailServiceStatus.IN_PROGRESS, progress); 117b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 118b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 119b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 120b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 121b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Encoder for Exchange 2003 attachment names. They come from the server partially encoded, 122b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * but there are still possible characters that need to be encoded (Why, MSFT, why?) 123b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 124b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private static class AttachmentNameEncoder extends UriCodec { 125b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee @Override 126b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee protected boolean isRetained(final char c) { 127b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // These four characters are commonly received in EAS 2.5 attachment names and are 128b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // valid (verified by testing); we won't encode them 129b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return c == '_' || c == ':' || c == '/' || c == '.'; 130b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 131b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 132b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 133b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 134b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Finish encoding attachment names for Exchange 2003. 135b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param str A partially encoded string. 136b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @return The fully encoded version of str. 137b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 138b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private static String encodeForExchange2003(final String str) { 139b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final AttachmentNameEncoder enc = new AttachmentNameEncoder(); 140b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final StringBuilder sb = new StringBuilder(str.length() + 16); 141b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee enc.appendPartiallyEncoded(sb, str); 142b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return sb.toString(); 143b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 144b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 145b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 146b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Finish encoding attachment names for Exchange 2003. 147b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @return A {@link EmailServiceStatus} code that indicates the result of the operation. 148b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 149b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee @Override 1508c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu public int performOperation() { 151b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mAttachment = EmailContent.Attachment.restoreAttachmentWithId(mContext, mAttachmentId); 152b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (mAttachment == null) { 153b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Could not load attachment %d", mAttachmentId); 154b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee doStatusCallback(mCallback, -1, mAttachmentId, EmailServiceStatus.ATTACHMENT_NOT_FOUND, 155b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 0); 156b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return RESULT_LOAD_ATTACHMENT_INFO_ERROR; 157b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 158b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (mAttachment.mLocation == null) { 159b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Attachment %d lacks a location", mAttachmentId); 160b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee doStatusCallback(mCallback, -1, mAttachmentId, EmailServiceStatus.ATTACHMENT_NOT_FOUND, 161b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 0); 162b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return RESULT_ATTACHMENT_NO_LOCATION_ERROR; 163b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 164b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final EmailContent.Message message = EmailContent.Message 165b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee .restoreMessageWithId(mContext, mAttachment.mMessageKey); 166b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (message == null) { 167b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Could not load message %d", mAttachment.mMessageKey); 168b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee doStatusCallback(mCallback, mAttachment.mMessageKey, mAttachmentId, 169b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee EmailServiceStatus.MESSAGE_NOT_FOUND, 0); 170b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return RESULT_ATTACHMENT_LOAD_MESSAGE_ERROR; 171b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 172b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 173b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // First callback to let the client know that we have started the attachment load. 1747848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee doStatusCallback(mCallback, mAttachment.mMessageKey, mAttachmentId, 175b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee EmailServiceStatus.IN_PROGRESS, 0); 176b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 177aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee final int result = super.performOperation(); 178b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 1797848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee // Last callback to report results. 180aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee if (result < 0) { 1817848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee // We had an error processing an attachment, let's report a {@link EmailServiceStatus} 1827848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee // connection error in this case 1837848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee LogUtils.d(LOG_TAG, "Invoking callback for attachmentId: %d with CONNECTION_ERROR", 1847848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee mAttachmentId); 1857848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee doStatusCallback(mCallback, mAttachment.mMessageKey, mAttachmentId, 1867848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee EmailServiceStatus.CONNECTION_ERROR, 0); 1877848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee } else { 1887848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee LogUtils.d(LOG_TAG, "Invoking callback for attachmentId: %d with SUCCESS", 1897848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee mAttachmentId); 1907848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee doStatusCallback(mCallback, mAttachment.mMessageKey, mAttachmentId, 1917848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee EmailServiceStatus.SUCCESS, 0); 192b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 193aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee return result; 194b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 195b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 196b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee @Override 197b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee protected String getCommand() { 198b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (mAttachment == null) { 199b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.wtf(LOG_TAG, "Error, mAttachment is null"); 200b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 201b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 202b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final String cmd; 203b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 204b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // The operation is different in EAS 14.0 than in earlier versions 205b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee cmd = "ItemOperations"; 206b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } else { 207b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final String location; 208b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // For Exchange 2003 (EAS 2.5), we have to look for illegal chars in the file name 209b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // that EAS sent to us! 210b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 211b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee location = encodeForExchange2003(mAttachment.mLocation); 212b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } else { 213b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee location = mAttachment.mLocation; 214b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 215b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee cmd = "GetAttachment&AttachmentName=" + location; 216b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 217b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return cmd; 218b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 219b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 220b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee @Override 221b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee protected HttpEntity getRequestEntity() throws IOException { 222b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (mAttachment == null) { 223b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.wtf(LOG_TAG, "Error, mAttachment is null"); 224b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 225b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 226b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final HttpEntity entity; 227b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final Serializer s = new Serializer(); 228b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 229b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee s.start(Tags.ITEMS_ITEMS).start(Tags.ITEMS_FETCH); 230b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee s.data(Tags.ITEMS_STORE, "Mailbox"); 231b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee s.data(Tags.BASE_FILE_REFERENCE, mAttachment.mLocation); 232b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee s.end().end().done(); // ITEMS_FETCH, ITEMS_ITEMS 233b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee entity = makeEntity(s); 234b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } else { 235b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Older versions of the protocol have the attachment location in the command. 236b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee entity = null; 237b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 238b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return entity; 239b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 240b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 241b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 242b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Close, ignoring errors (as during cleanup) 243b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param c a Closeable 244b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 245b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private static void close(final Closeable c) { 246b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 247b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee c.close(); 248b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (IOException e) { 249b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "IOException while cleaning up attachment: %s", e.getMessage()); 250b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 251b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 252b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 253b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 254b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Save away the contentUri for this Attachment and notify listeners 255b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 256b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee private boolean finishLoadAttachment(final EmailContent.Attachment attachment, final File file) { 257b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final InputStream in; 258b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 259b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee in = new FileInputStream(file); 260b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (final FileNotFoundException e) { 261b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Unlikely, as we just created it successfully, but log it. 262b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Could not open attachment file: %s", e.getMessage()); 263b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return false; 264b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 265b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee AttachmentUtilities.saveAttachment(mContext, in, attachment); 266b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee close(in); 267b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return true; 268b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 269b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 270b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee /** 271b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * Read the {@link EasResponse} and extract the attachment data, saving it to the provider. 272b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee * @param response The (successful) {@link EasResponse} containing the attachment data. 2737848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee * @return A status code, 0 is a success, anything negative is an error outlined by constants 2747848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee * in this class or its base class. 275b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee */ 276b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee @Override 2778c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu protected int handleResponse(final EasResponse response) { 278b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Some very basic error checking on the response object first. 279b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Our base class should be responsible for checking these errors but if the error 280b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // checking is done in the override functions, we can be more specific about 281b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // the errors that are being returned to the caller of performOperation(). 282b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (response.isEmpty()) { 283b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Error, empty response."); 28461ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon return RESULT_NETWORK_PROBLEM; 285b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 286b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 287b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // This is a 2 step process. 288b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // 1. Grab what came over the wire and write it to a temp file on disk. 289b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // 2. Move the attachment to its final location. 290b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final File tmpFile; 291b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 292b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee tmpFile = File.createTempFile("eas_", "tmp", mContext.getCacheDir()); 293b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (final IOException e) { 294b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Could not open temp file: %s", e.getMessage()); 29561ebb38e67421fd122c81f046bf11778b61a2113Martin Hibdon return RESULT_NETWORK_PROBLEM; 296b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 297b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee 298b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 299b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final OutputStream os; 300b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 301b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee os = new FileOutputStream(tmpFile); 302b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (final FileNotFoundException e) { 303b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Temp file not found: %s", e.getMessage()); 3047848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee return RESULT_ATTACHMENT_INTERNAL_HANDLING_ERROR; 305b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 306b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 307b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final InputStream is = response.getInputStream(); 308b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee try { 309b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // TODO: Right now we are explictly loading this from a class 310b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // that will be deprecated when we move over to EasService. When we start using 311b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // our internal class instead, there will be rippling side effect changes that 312b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // need to be made when this time comes. 313b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final ProgressCallback callback = new ProgressCallback(mCallback, mAttachment); 314b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final boolean success; 315b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) { 316b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final ItemOperationsParser parser = new ItemOperationsParser(is, os, 317b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee mAttachment.mSize, callback); 318b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee parser.parse(); 319b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee success = (parser.getStatusCode() == 1); 320b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } else { 321b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final int length = response.getLength(); 322b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (length != 0) { 323b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // len > 0 means that Content-Length was set in the headers 324b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // len < 0 means "chunked" transfer-encoding 325b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee ItemOperationsParser.readChunked(is, os, 326b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee (length < 0) ? mAttachment.mSize : length, callback); 327b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 328b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee success = true; 329b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 330b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Check that we successfully grabbed what came over the wire... 331b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (!success) { 332b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Error parsing server response"); 3337848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee return RESULT_ATTACHMENT_RESPONSE_PARSING_ERROR; 334b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 335b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee // Now finish the process and save to the final destination. 336b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee final boolean loadResult = finishLoadAttachment(mAttachment, tmpFile); 337b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee if (!loadResult) { 338b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Error post processing attachment file."); 3397848f4e9c1a50fa36ddcce692b11ee713df14cb8Anthony Lee return RESULT_ATTACHMENT_INTERNAL_HANDLING_ERROR; 340b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 341b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } catch (final IOException e) { 342b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee LogUtils.e(LOG_TAG, "Error handling attachment: %s", e.getMessage()); 343b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee return RESULT_ATTACHMENT_INTERNAL_HANDLING_ERROR; 344b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } finally { 345b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee close(is); 346b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 347b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } finally { 348b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee close(os); 349b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 350b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } finally { 351b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee tmpFile.delete(); 352b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 353aeaf8f3a7277eb31d42d31a1df52f900f9ce1ec8Anthony Lee return RESULT_SUCCESS; 354b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee } 355b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee} 356