198 lines
7.0 KiB
Java
198 lines
7.0 KiB
Java
/* Copyright (C) 2012 The Android Open Source Project.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.imap2;
|
|
|
|
import android.content.Context;
|
|
import android.os.RemoteException;
|
|
|
|
import com.android.emailcommon.provider.EmailContent.Attachment;
|
|
import com.android.emailcommon.provider.EmailContent.Message;
|
|
import com.android.emailcommon.service.EmailServiceStatus;
|
|
import com.android.emailsync.PartRequest;
|
|
import com.android.emailcommon.utility.AttachmentUtilities;
|
|
import com.android.imap2.Imap2SyncService.Connection;
|
|
import com.android.mail.providers.UIProvider;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
|
|
/**
|
|
* Handle IMAP2 attachment loading
|
|
*/
|
|
public class AttachmentLoader {
|
|
static private final int CHUNK_SIZE = 16*1024;
|
|
|
|
private final Context mContext;
|
|
private final Attachment mAttachment;
|
|
private final long mAttachmentId;
|
|
private final long mMessageId;
|
|
private final Message mMessage;
|
|
private final Imap2SyncService mService;
|
|
|
|
public AttachmentLoader(Imap2SyncService service, PartRequest req) {
|
|
mService = service;
|
|
mContext = service.mContext;
|
|
mAttachment = req.mAttachment;
|
|
mAttachmentId = mAttachment.mId;
|
|
mMessageId = mAttachment.mMessageKey;
|
|
mMessage = Message.restoreMessageWithId(mContext, mMessageId);
|
|
}
|
|
|
|
private void doStatusCallback(int status) {
|
|
try {
|
|
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId, status, 0);
|
|
} catch (RemoteException e) {
|
|
// No danger if the client is no longer around
|
|
}
|
|
}
|
|
|
|
private void doProgressCallback(int progress) {
|
|
try {
|
|
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId,
|
|
EmailServiceStatus.IN_PROGRESS, progress);
|
|
} catch (RemoteException e) {
|
|
// No danger if the client is no longer around
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close, ignoring errors (as during cleanup)
|
|
* @param c a Closeable
|
|
*/
|
|
private void close(Closeable c) {
|
|
try {
|
|
c.close();
|
|
} catch (IOException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save away the contentUri for this Attachment and notify listeners
|
|
* @throws IOException
|
|
*/
|
|
private void finishLoadAttachment(File file, OutputStream os) throws IOException {
|
|
InputStream in = null;
|
|
try {
|
|
in = new FileInputStream(file);
|
|
AttachmentUtilities.saveAttachment(mContext, in, mAttachment);
|
|
doStatusCallback(EmailServiceStatus.SUCCESS);
|
|
} catch (FileNotFoundException e) {
|
|
// Not bloody likely, as we just created it successfully
|
|
throw new IOException("Attachment file not found?");
|
|
} finally {
|
|
close(in);
|
|
}
|
|
}
|
|
|
|
private void readPart (ImapInputStream in, String tag, OutputStream out) throws IOException {
|
|
String res = in.readLine();
|
|
int bstart = res.indexOf("body[");
|
|
if (bstart < 0)
|
|
bstart = res.indexOf("BODY[");
|
|
if (bstart < 0)
|
|
return;
|
|
int bend = res.indexOf(']', bstart);
|
|
if (bend < 0)
|
|
return;
|
|
int br = res.indexOf('{');
|
|
if (br > 0) {
|
|
Parser p = new Parser(res, br + 1);
|
|
int expectedLength = p.parseInteger();
|
|
int remainingLength = expectedLength;
|
|
int totalRead = 0;
|
|
byte[] buf = new byte[CHUNK_SIZE];
|
|
int lastCallbackPct = -1;
|
|
int lastCallbackTotalRead = 0;
|
|
while (remainingLength > 0) {
|
|
int rdlen = (remainingLength > CHUNK_SIZE ? CHUNK_SIZE : remainingLength);
|
|
int bytesRead = in.read(buf, 0, rdlen);
|
|
totalRead += bytesRead;
|
|
out.write(buf, 0, bytesRead);
|
|
remainingLength -= bytesRead;
|
|
int pct = (totalRead * 100) / expectedLength;
|
|
// Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
|
|
// We don't want to spam the Email app
|
|
if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
|
|
// Report progress back to the UI
|
|
doProgressCallback(pct);
|
|
lastCallbackTotalRead = totalRead;
|
|
lastCallbackPct = pct;
|
|
}
|
|
}
|
|
out.close();
|
|
String line = in.readLine();
|
|
if (!line.endsWith(")")) {
|
|
mService.errorLog("Bad part?");
|
|
throw new IOException();
|
|
}
|
|
line = in.readLine();
|
|
if (!line.startsWith(tag)) {
|
|
mService.userLog("Bad part?");
|
|
throw new IOException();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads an attachment, based on the PartRequest passed in the constructor
|
|
* @throws IOException
|
|
*/
|
|
public void loadAttachment(Connection conn) throws IOException {
|
|
if (mMessage == null) {
|
|
doStatusCallback(EmailServiceStatus.MESSAGE_NOT_FOUND);
|
|
return;
|
|
}
|
|
if (mAttachment.mUiState == UIProvider.AttachmentState.SAVED) {
|
|
return;
|
|
}
|
|
// Say we've started loading the attachment
|
|
doProgressCallback(0);
|
|
|
|
try {
|
|
OutputStream os = null;
|
|
File tmpFile = null;
|
|
try {
|
|
tmpFile = File.createTempFile("imap2_", "tmp", mContext.getCacheDir());
|
|
os = new FileOutputStream(tmpFile);
|
|
String tag = mService.writeCommand(conn.writer, "uid fetch " + mMessage.mServerId +
|
|
" body[" + mAttachment.mLocation + ']');
|
|
readPart(conn.reader, tag, os);
|
|
finishLoadAttachment(tmpFile, os);
|
|
return;
|
|
} catch (FileNotFoundException e) {
|
|
mService.errorLog("Can't get attachment; write file not found?");
|
|
doStatusCallback(EmailServiceStatus.ATTACHMENT_NOT_FOUND);
|
|
} finally {
|
|
close(os);
|
|
if (tmpFile != null) {
|
|
tmpFile.delete();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
// Report the error, but also report back to the service
|
|
doStatusCallback(EmailServiceStatus.CONNECTION_ERROR);
|
|
throw e;
|
|
} finally {
|
|
}
|
|
}
|
|
}
|