15c523858385176c33a7456bb84035de78552d22dMarc Blank/*
25c523858385176c33a7456bb84035de78552d22dMarc Blank * Copyright (C) 2010 The Android Open Source Project
35c523858385176c33a7456bb84035de78552d22dMarc Blank *
45c523858385176c33a7456bb84035de78552d22dMarc Blank * Licensed under the Apache License, Version 2.0 (the "License");
55c523858385176c33a7456bb84035de78552d22dMarc Blank * you may not use this file except in compliance with the License.
65c523858385176c33a7456bb84035de78552d22dMarc Blank * You may obtain a copy of the License at
75c523858385176c33a7456bb84035de78552d22dMarc Blank *
85c523858385176c33a7456bb84035de78552d22dMarc Blank *      http://www.apache.org/licenses/LICENSE-2.0
95c523858385176c33a7456bb84035de78552d22dMarc Blank *
105c523858385176c33a7456bb84035de78552d22dMarc Blank * Unless required by applicable law or agreed to in writing, software
115c523858385176c33a7456bb84035de78552d22dMarc Blank * distributed under the License is distributed on an "AS IS" BASIS,
125c523858385176c33a7456bb84035de78552d22dMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135c523858385176c33a7456bb84035de78552d22dMarc Blank * See the License for the specific language governing permissions and
145c523858385176c33a7456bb84035de78552d22dMarc Blank * limitations under the License.
155c523858385176c33a7456bb84035de78552d22dMarc Blank */
165c523858385176c33a7456bb84035de78552d22dMarc Blank
175c523858385176c33a7456bb84035de78552d22dMarc Blankpackage com.android.email.service;
185c523858385176c33a7456bb84035de78552d22dMarc Blank
195c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.FixedLengthInputStream;
205c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.mail.store.imap.ImapResponse;
215c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.mail.store.imap.ImapResponseParser;
225c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.mail.store.imap.ImapString;
235c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.Logging;
245c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.TempDirectory;
255c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.utility.Utility;
26560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils;
275c523858385176c33a7456bb84035de78552d22dMarc Blank
285c523858385176c33a7456bb84035de78552d22dMarc Blankimport org.apache.commons.io.IOUtils;
295c523858385176c33a7456bb84035de78552d22dMarc Blank
305c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.ByteArrayInputStream;
315c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.File;
325c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileInputStream;
335c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileNotFoundException;
345c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileOutputStream;
355c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.IOException;
365c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.InputStream;
375c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.OutputStream;
385c523858385176c33a7456bb84035de78552d22dMarc Blank
395c523858385176c33a7456bb84035de78552d22dMarc Blank/**
405c523858385176c33a7456bb84035de78552d22dMarc Blank * Subclass of {@link ImapString} used for literals backed by a temp file.
415c523858385176c33a7456bb84035de78552d22dMarc Blank */
425c523858385176c33a7456bb84035de78552d22dMarc Blankpublic class ImapTempFileLiteral extends ImapString {
435c523858385176c33a7456bb84035de78552d22dMarc Blank    /* package for test */ final File mFile;
445c523858385176c33a7456bb84035de78552d22dMarc Blank
455c523858385176c33a7456bb84035de78552d22dMarc Blank    /** Size is purely for toString() */
465c523858385176c33a7456bb84035de78552d22dMarc Blank    private final int mSize;
475c523858385176c33a7456bb84035de78552d22dMarc Blank
485c523858385176c33a7456bb84035de78552d22dMarc Blank    /* package */  ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
495c523858385176c33a7456bb84035de78552d22dMarc Blank        mSize = stream.getLength();
505c523858385176c33a7456bb84035de78552d22dMarc Blank        mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
515c523858385176c33a7456bb84035de78552d22dMarc Blank
525c523858385176c33a7456bb84035de78552d22dMarc Blank        // Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
535c523858385176c33a7456bb84035de78552d22dMarc Blank        // so it'd simply cause a memory leak.
545c523858385176c33a7456bb84035de78552d22dMarc Blank        // deleteOnExit() simply adds filenames to a static list and the list will never shrink.
555c523858385176c33a7456bb84035de78552d22dMarc Blank        // mFile.deleteOnExit();
565c523858385176c33a7456bb84035de78552d22dMarc Blank        OutputStream out = new FileOutputStream(mFile);
575c523858385176c33a7456bb84035de78552d22dMarc Blank        IOUtils.copy(stream, out);
585c523858385176c33a7456bb84035de78552d22dMarc Blank        out.close();
595c523858385176c33a7456bb84035de78552d22dMarc Blank    }
605c523858385176c33a7456bb84035de78552d22dMarc Blank
615c523858385176c33a7456bb84035de78552d22dMarc Blank    /**
625c523858385176c33a7456bb84035de78552d22dMarc Blank     * Make sure we delete the temp file.
635c523858385176c33a7456bb84035de78552d22dMarc Blank     *
645c523858385176c33a7456bb84035de78552d22dMarc Blank     * We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
655c523858385176c33a7456bb84035de78552d22dMarc Blank     */
665c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
675c523858385176c33a7456bb84035de78552d22dMarc Blank    protected void finalize() throws Throwable {
685c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
695c523858385176c33a7456bb84035de78552d22dMarc Blank            destroy();
705c523858385176c33a7456bb84035de78552d22dMarc Blank        } finally {
715c523858385176c33a7456bb84035de78552d22dMarc Blank            super.finalize();
725c523858385176c33a7456bb84035de78552d22dMarc Blank        }
735c523858385176c33a7456bb84035de78552d22dMarc Blank    }
745c523858385176c33a7456bb84035de78552d22dMarc Blank
755c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
765c523858385176c33a7456bb84035de78552d22dMarc Blank    public InputStream getAsStream() {
775c523858385176c33a7456bb84035de78552d22dMarc Blank        checkNotDestroyed();
785c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
795c523858385176c33a7456bb84035de78552d22dMarc Blank            return new FileInputStream(mFile);
805c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (FileNotFoundException e) {
815c523858385176c33a7456bb84035de78552d22dMarc Blank            // It's probably possible if we're low on storage and the system clears the cache dir.
82560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "ImapTempFileLiteral: Temp file not found");
835c523858385176c33a7456bb84035de78552d22dMarc Blank
845c523858385176c33a7456bb84035de78552d22dMarc Blank            // Return 0 byte stream as a dummy...
855c523858385176c33a7456bb84035de78552d22dMarc Blank            return new ByteArrayInputStream(new byte[0]);
865c523858385176c33a7456bb84035de78552d22dMarc Blank        }
875c523858385176c33a7456bb84035de78552d22dMarc Blank    }
885c523858385176c33a7456bb84035de78552d22dMarc Blank
895c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
905c523858385176c33a7456bb84035de78552d22dMarc Blank    public String getString() {
915c523858385176c33a7456bb84035de78552d22dMarc Blank        checkNotDestroyed();
925c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
935c523858385176c33a7456bb84035de78552d22dMarc Blank            byte[] bytes = IOUtils.toByteArray(getAsStream());
945c523858385176c33a7456bb84035de78552d22dMarc Blank            // Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
955c523858385176c33a7456bb84035de78552d22dMarc Blank            if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
965c523858385176c33a7456bb84035de78552d22dMarc Blank                throw new IOException();
975c523858385176c33a7456bb84035de78552d22dMarc Blank            }
985c523858385176c33a7456bb84035de78552d22dMarc Blank            return Utility.fromAscii(bytes);
995c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (IOException e) {
100560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "ImapTempFileLiteral: Error while reading temp file", e);
1015c523858385176c33a7456bb84035de78552d22dMarc Blank            return "";
1025c523858385176c33a7456bb84035de78552d22dMarc Blank        }
1035c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1045c523858385176c33a7456bb84035de78552d22dMarc Blank
1055c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
1065c523858385176c33a7456bb84035de78552d22dMarc Blank    public void destroy() {
1075c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
1085c523858385176c33a7456bb84035de78552d22dMarc Blank            if (!isDestroyed() && mFile.exists()) {
1095c523858385176c33a7456bb84035de78552d22dMarc Blank                mFile.delete();
1105c523858385176c33a7456bb84035de78552d22dMarc Blank            }
1115c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (RuntimeException re) {
1125c523858385176c33a7456bb84035de78552d22dMarc Blank            // Just log and ignore.
113560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "Failed to remove temp file: " + re.getMessage());
1145c523858385176c33a7456bb84035de78552d22dMarc Blank        }
1155c523858385176c33a7456bb84035de78552d22dMarc Blank        super.destroy();
1165c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1175c523858385176c33a7456bb84035de78552d22dMarc Blank
1185c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
1195c523858385176c33a7456bb84035de78552d22dMarc Blank    public String toString() {
1205c523858385176c33a7456bb84035de78552d22dMarc Blank        return String.format("{%d byte literal(file)}", mSize);
1215c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1225c523858385176c33a7456bb84035de78552d22dMarc Blank
1235c523858385176c33a7456bb84035de78552d22dMarc Blank    public boolean tempFileExistsForTest() {
1245c523858385176c33a7456bb84035de78552d22dMarc Blank        return mFile.exists();
1255c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1265c523858385176c33a7456bb84035de78552d22dMarc Blank}
127