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.mail.store.imap;
185c523858385176c33a7456bb84035de78552d22dMarc Blank
195c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.FixedLengthInputStream;
205c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.Logging;
215c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.TempDirectory;
225c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.utility.Utility;
23560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils;
245c523858385176c33a7456bb84035de78552d22dMarc Blank
255c523858385176c33a7456bb84035de78552d22dMarc Blankimport org.apache.commons.io.IOUtils;
265c523858385176c33a7456bb84035de78552d22dMarc Blank
275c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.ByteArrayInputStream;
285c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.File;
295c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileInputStream;
305c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileNotFoundException;
315c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.FileOutputStream;
325c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.IOException;
335c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.InputStream;
345c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.io.OutputStream;
355c523858385176c33a7456bb84035de78552d22dMarc Blank
365c523858385176c33a7456bb84035de78552d22dMarc Blank/**
375c523858385176c33a7456bb84035de78552d22dMarc Blank * Subclass of {@link ImapString} used for literals backed by a temp file.
385c523858385176c33a7456bb84035de78552d22dMarc Blank */
395c523858385176c33a7456bb84035de78552d22dMarc Blankpublic class ImapTempFileLiteral extends ImapString {
405c523858385176c33a7456bb84035de78552d22dMarc Blank    /* package for test */ final File mFile;
415c523858385176c33a7456bb84035de78552d22dMarc Blank
425c523858385176c33a7456bb84035de78552d22dMarc Blank    /** Size is purely for toString() */
435c523858385176c33a7456bb84035de78552d22dMarc Blank    private final int mSize;
445c523858385176c33a7456bb84035de78552d22dMarc Blank
455c523858385176c33a7456bb84035de78552d22dMarc Blank    /* package */  ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
465c523858385176c33a7456bb84035de78552d22dMarc Blank        mSize = stream.getLength();
475c523858385176c33a7456bb84035de78552d22dMarc Blank        mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
485c523858385176c33a7456bb84035de78552d22dMarc Blank
495c523858385176c33a7456bb84035de78552d22dMarc Blank        // Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
505c523858385176c33a7456bb84035de78552d22dMarc Blank        // so it'd simply cause a memory leak.
515c523858385176c33a7456bb84035de78552d22dMarc Blank        // deleteOnExit() simply adds filenames to a static list and the list will never shrink.
525c523858385176c33a7456bb84035de78552d22dMarc Blank        // mFile.deleteOnExit();
535c523858385176c33a7456bb84035de78552d22dMarc Blank        OutputStream out = new FileOutputStream(mFile);
545c523858385176c33a7456bb84035de78552d22dMarc Blank        IOUtils.copy(stream, out);
555c523858385176c33a7456bb84035de78552d22dMarc Blank        out.close();
565c523858385176c33a7456bb84035de78552d22dMarc Blank    }
575c523858385176c33a7456bb84035de78552d22dMarc Blank
585c523858385176c33a7456bb84035de78552d22dMarc Blank    /**
595c523858385176c33a7456bb84035de78552d22dMarc Blank     * Make sure we delete the temp file.
605c523858385176c33a7456bb84035de78552d22dMarc Blank     *
615c523858385176c33a7456bb84035de78552d22dMarc Blank     * We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
625c523858385176c33a7456bb84035de78552d22dMarc Blank     */
635c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
645c523858385176c33a7456bb84035de78552d22dMarc Blank    protected void finalize() throws Throwable {
655c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
665c523858385176c33a7456bb84035de78552d22dMarc Blank            destroy();
675c523858385176c33a7456bb84035de78552d22dMarc Blank        } finally {
685c523858385176c33a7456bb84035de78552d22dMarc Blank            super.finalize();
695c523858385176c33a7456bb84035de78552d22dMarc Blank        }
705c523858385176c33a7456bb84035de78552d22dMarc Blank    }
715c523858385176c33a7456bb84035de78552d22dMarc Blank
725c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
735c523858385176c33a7456bb84035de78552d22dMarc Blank    public InputStream getAsStream() {
745c523858385176c33a7456bb84035de78552d22dMarc Blank        checkNotDestroyed();
755c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
765c523858385176c33a7456bb84035de78552d22dMarc Blank            return new FileInputStream(mFile);
775c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (FileNotFoundException e) {
785c523858385176c33a7456bb84035de78552d22dMarc Blank            // It's probably possible if we're low on storage and the system clears the cache dir.
79560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "ImapTempFileLiteral: Temp file not found");
805c523858385176c33a7456bb84035de78552d22dMarc Blank
815c523858385176c33a7456bb84035de78552d22dMarc Blank            // Return 0 byte stream as a dummy...
825c523858385176c33a7456bb84035de78552d22dMarc Blank            return new ByteArrayInputStream(new byte[0]);
835c523858385176c33a7456bb84035de78552d22dMarc Blank        }
845c523858385176c33a7456bb84035de78552d22dMarc Blank    }
855c523858385176c33a7456bb84035de78552d22dMarc Blank
865c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
875c523858385176c33a7456bb84035de78552d22dMarc Blank    public String getString() {
885c523858385176c33a7456bb84035de78552d22dMarc Blank        checkNotDestroyed();
895c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
905c523858385176c33a7456bb84035de78552d22dMarc Blank            byte[] bytes = IOUtils.toByteArray(getAsStream());
915c523858385176c33a7456bb84035de78552d22dMarc Blank            // Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
925c523858385176c33a7456bb84035de78552d22dMarc Blank            if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
935c523858385176c33a7456bb84035de78552d22dMarc Blank                throw new IOException();
945c523858385176c33a7456bb84035de78552d22dMarc Blank            }
955c523858385176c33a7456bb84035de78552d22dMarc Blank            return Utility.fromAscii(bytes);
965c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (IOException e) {
97560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "ImapTempFileLiteral: Error while reading temp file", e);
985c523858385176c33a7456bb84035de78552d22dMarc Blank            return "";
995c523858385176c33a7456bb84035de78552d22dMarc Blank        }
1005c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1015c523858385176c33a7456bb84035de78552d22dMarc Blank
1025c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
1035c523858385176c33a7456bb84035de78552d22dMarc Blank    public void destroy() {
1045c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
1055c523858385176c33a7456bb84035de78552d22dMarc Blank            if (!isDestroyed() && mFile.exists()) {
1065c523858385176c33a7456bb84035de78552d22dMarc Blank                mFile.delete();
1075c523858385176c33a7456bb84035de78552d22dMarc Blank            }
1085c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (RuntimeException re) {
1095c523858385176c33a7456bb84035de78552d22dMarc Blank            // Just log and ignore.
110560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "Failed to remove temp file: " + re.getMessage());
1115c523858385176c33a7456bb84035de78552d22dMarc Blank        }
1125c523858385176c33a7456bb84035de78552d22dMarc Blank        super.destroy();
1135c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1145c523858385176c33a7456bb84035de78552d22dMarc Blank
1155c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
1165c523858385176c33a7456bb84035de78552d22dMarc Blank    public String toString() {
1175c523858385176c33a7456bb84035de78552d22dMarc Blank        return String.format("{%d byte literal(file)}", mSize);
1185c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1195c523858385176c33a7456bb84035de78552d22dMarc Blank
1205c523858385176c33a7456bb84035de78552d22dMarc Blank    public boolean tempFileExistsForTest() {
1215c523858385176c33a7456bb84035de78552d22dMarc Blank        return mFile.exists();
1225c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1235c523858385176c33a7456bb84035de78552d22dMarc Blank}
124