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