• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2011 The Android Open Source Project.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.android.exchange.adapter;
17 
18 import android.content.Context;
19 
20 import com.android.emailcommon.provider.EmailContent.Attachment;
21 import com.android.emailcommon.provider.EmailContent.Message;
22 import com.android.emailcommon.service.EmailServiceStatus;
23 import com.android.emailcommon.utility.AttachmentUtilities;
24 import com.android.emailsync.PartRequest;
25 import com.android.exchange.Eas;
26 import com.android.exchange.EasResponse;
27 import com.android.exchange.EasSyncService;
28 import com.android.exchange.utility.UriCodec;
29 import com.google.common.annotations.VisibleForTesting;
30 
31 import org.apache.http.HttpStatus;
32 
33 import java.io.Closeable;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.FileNotFoundException;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 
42 /**
43  * Handle EAS attachment loading, regardless of protocol version
44  */
45 public class AttachmentLoader {
46     private final EasSyncService mService;
47     private final Context mContext;
48     private final Attachment mAttachment;
49     private final long mAttachmentId;
50     private final int mAttachmentSize;
51     private final long mMessageId;
52     private final Message mMessage;
53 
AttachmentLoader(EasSyncService service, PartRequest req)54     public AttachmentLoader(EasSyncService service, PartRequest req) {
55         mService = service;
56         mContext = service.mContext;
57         mAttachment = req.mAttachment;
58         mAttachmentId = mAttachment.mId;
59         mAttachmentSize = (int)mAttachment.mSize;
60         mMessageId = mAttachment.mMessageKey;
61         mMessage = Message.restoreMessageWithId(mContext, mMessageId);
62     }
63 
doStatusCallback(int status)64     private void doStatusCallback(int status) {
65 
66     }
67 
doProgressCallback(int progress)68     private void doProgressCallback(int progress) {
69 
70     }
71 
72     @VisibleForTesting
encodeForExchange2003(String str)73     static String encodeForExchange2003(String str) {
74         AttachmentNameEncoder enc = new AttachmentNameEncoder();
75         StringBuilder sb = new StringBuilder(str.length() + 16);
76         enc.appendPartiallyEncoded(sb, str);
77         return sb.toString();
78     }
79 
80     /**
81      * Encoder for Exchange 2003 attachment names.  They come from the server partially encoded,
82      * but there are still possible characters that need to be encoded (Why, MSFT, why?)
83      */
84     private static class AttachmentNameEncoder extends UriCodec {
isRetained(char c)85         @Override protected boolean isRetained(char c) {
86             // These four characters are commonly received in EAS 2.5 attachment names and are
87             // valid (verified by testing); we won't encode them
88             return c == '_' || c == ':' || c == '/' || c == '.';
89         }
90     }
91 
92     /**
93      * Close, ignoring errors (as during cleanup)
94      * @param c a Closeable
95      */
close(Closeable c)96     private static void close(Closeable c) {
97         try {
98             c.close();
99         } catch (IOException e) {
100         }
101     }
102 
103     /**
104      * Save away the contentUri for this Attachment and notify listeners
105      * @throws IOException
106      */
finishLoadAttachment(File file)107     private void finishLoadAttachment(File file) throws IOException {
108         InputStream in = null;
109         try {
110             in = new FileInputStream(file);
111             AttachmentUtilities.saveAttachment(mContext, in, mAttachment);
112             doStatusCallback(EmailServiceStatus.SUCCESS);
113         } catch (FileNotFoundException e) {
114             // Not bloody likely, as we just created it successfully
115             throw new IOException("Attachment file not found?");
116         } finally {
117             close(in);
118         }
119     }
120 
121     /**
122      * Loads an attachment, based on the PartRequest passed in the constructor
123      * @throws IOException
124      */
loadAttachment()125     public void loadAttachment() throws IOException {
126         if (mMessage == null) {
127             doStatusCallback(EmailServiceStatus.MESSAGE_NOT_FOUND);
128             return;
129         }
130         // Say we've started loading the attachment
131         doProgressCallback(0);
132 
133         EasResponse resp = null;
134         // The method of attachment loading is different in EAS 14.0 than in earlier versions
135         boolean eas14 = mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE;
136         try {
137             if (eas14) {
138                 Serializer s = new Serializer();
139                 s.start(Tags.ITEMS_ITEMS).start(Tags.ITEMS_FETCH);
140                 s.data(Tags.ITEMS_STORE, "Mailbox");
141                 s.data(Tags.BASE_FILE_REFERENCE, mAttachment.mLocation);
142                 s.end().end().done(); // ITEMS_FETCH, ITEMS_ITEMS
143                 resp = mService.sendHttpClientPost("ItemOperations", s.toByteArray());
144             } else {
145                 String location = mAttachment.mLocation;
146                 // For Exchange 2003 (EAS 2.5), we have to look for illegal chars in the file name
147                 // that EAS sent to us!
148                 if (mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
149                     location = encodeForExchange2003(location);
150                 }
151                 String cmd = "GetAttachment&AttachmentName=" + location;
152                 resp = mService.sendHttpClientPost(cmd, null, EasSyncService.COMMAND_TIMEOUT);
153             }
154 
155             int status = resp.getStatus();
156             if (status == HttpStatus.SC_OK) {
157                 if (!resp.isEmpty()) {
158                     InputStream is = resp.getInputStream();
159                     OutputStream os = null;
160                     File tmpFile = null;
161                     try {
162                         tmpFile = File.createTempFile("eas_", "tmp", mContext.getCacheDir());
163                         os = new FileOutputStream(tmpFile);
164                         if (eas14) {
165                             ItemOperationsParser p = new ItemOperationsParser(is, os,
166                                     mAttachmentSize, null);
167                             p.parse();
168                             if (p.getStatusCode() == 1 /* Success */) {
169                                 finishLoadAttachment(tmpFile);
170                                 return;
171                             }
172                         } else {
173                             int len = resp.getLength();
174                             if (len != 0) {
175                                 // len > 0 means that Content-Length was set in the headers
176                                 // len < 0 means "chunked" transfer-encoding
177                                 ItemOperationsParser.readChunked(is, os,
178                                         (len < 0) ? mAttachmentSize : len, null);
179                                 finishLoadAttachment(tmpFile);
180                                 return;
181                             }
182                         }
183                     } catch (FileNotFoundException e) {
184                         mService.errorLog("Can't get attachment; write file not found?");
185                         doStatusCallback(EmailServiceStatus.ATTACHMENT_NOT_FOUND);
186                     } finally {
187                         close(is);
188                         close(os);
189                         if (tmpFile != null) {
190                             tmpFile.delete();
191                         }
192                     }
193                 }
194             }
195         } catch (IOException e) {
196             // Report the error, but also report back to the service
197             doStatusCallback(EmailServiceStatus.CONNECTION_ERROR);
198             throw e;
199         } finally {
200             if (resp != null) {
201                 resp.close();
202             }
203         }
204     }
205 }
206