mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-09 20:58:07 -05:00
Merge branch 'master' into issue-162
This commit is contained in:
commit
14a0fdf27d
@ -1033,6 +1033,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="save_or_discard_draft_message_dlg_title">Save draft message?</string>
|
||||
<string name="save_or_discard_draft_message_instructions_fmt">Save or Discard this message?</string>
|
||||
|
||||
<string name="confirm_discard_draft_message_title">Discard message?</string>
|
||||
<string name="confirm_discard_draft_message">Are you sure you want to discard this message?</string>
|
||||
|
||||
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Refuse to save draft message.</string>
|
||||
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Refuse to save draft message marked encrypted.</string>
|
||||
|
||||
|
@ -170,6 +170,10 @@
|
||||
<incoming uri="pop3://incoming.verizon.net" username="$user" />
|
||||
<outgoing uri="smtp://outgoing.verizon.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="montclair.edu" label="MSU" domain="montclair.edu">
|
||||
<incoming uri="imap+ssl+://mail.montclair.edu" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.montclair.edu" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Yahoo! Mail Variants -->
|
||||
<provider id="yahoo" label="Yahoo" domain="yahoo.com">
|
||||
|
@ -83,6 +83,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
|
||||
private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2;
|
||||
private static final int DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY = 3;
|
||||
private static final int DIALOG_CONFIRM_DISCARD_ON_BACK = 4;
|
||||
|
||||
private static final long INVALID_DRAFT_ID = MessagingController.INVALID_MESSAGE_ID;
|
||||
|
||||
@ -2051,13 +2052,21 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
|
||||
} else if (!mDraftNeedsSaving || isDraftsFolderDisabled()) {
|
||||
Toast.makeText(MessageCompose.this, getString(R.string.message_discarded_toast), Toast.LENGTH_LONG).show();
|
||||
super.onBackPressed();
|
||||
if (mDraftNeedsSaving) {
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
|
||||
} else if (isDraftsFolderDisabled()) {
|
||||
showDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
} else {
|
||||
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
|
||||
// Check if editing an existing draft.
|
||||
if (mDraftId == INVALID_DRAFT_ID) {
|
||||
onDiscard();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2118,6 +2127,27 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
}
|
||||
})
|
||||
.create();
|
||||
case DIALOG_CONFIRM_DISCARD_ON_BACK:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.confirm_discard_draft_message_title)
|
||||
.setMessage(R.string.confirm_discard_draft_message)
|
||||
.setPositiveButton(R.string.cancel_action, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
dismissDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.discard_action, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
dismissDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
|
||||
Toast.makeText(MessageCompose.this,
|
||||
getString(R.string.message_discarded_toast),
|
||||
Toast.LENGTH_LONG).show();
|
||||
onDiscard();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
@ -2988,9 +3018,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
}
|
||||
|
||||
MessagingController.getInstance(getApplication()).sendMessage(mAccount, message, null);
|
||||
if (mDraftId != INVALID_DRAFT_ID) {
|
||||
MessagingController.getInstance(getApplication()).deleteDraft(mAccount, mDraftId);
|
||||
long draftId = mDraftId;
|
||||
if (draftId != INVALID_DRAFT_ID) {
|
||||
mDraftId = INVALID_DRAFT_ID;
|
||||
MessagingController.getInstance(getApplication()).deleteDraft(mAccount, draftId);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -20,6 +20,7 @@ import com.fsck.k9.crypto.PgpData;
|
||||
import com.fsck.k9.helper.FileBrowserHelper;
|
||||
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.view.AttachmentView;
|
||||
import com.fsck.k9.view.ToggleScrollView;
|
||||
@ -712,10 +713,15 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
private void onFlag() {
|
||||
if (mMessage != null) {
|
||||
mController.setFlag(mAccount,
|
||||
mMessage.getFolder().getName(), new String[] {mMessage.getUid()}, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
|
||||
boolean newState = !mMessage.isSet(Flag.FLAGGED);
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getName(),
|
||||
new String[] {mMessage.getUid()}, Flag.FLAGGED, newState);
|
||||
try {
|
||||
mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
|
||||
// FIXME: This is a hack to change the flagged state of our message object. We
|
||||
// can't call Message.setFlag() because that would "adjust" the flagged count
|
||||
// another time (first time by MessagingController.setFlag(...)).
|
||||
((LocalMessage)mMessage).setFlagInternal(Flag.FLAGGED, newState);
|
||||
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
} catch (MessagingException me) {
|
||||
Log.e(K9.LOG_TAG, "Could not set flag on local message", me);
|
||||
@ -848,9 +854,13 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
private void onMarkAsUnread() {
|
||||
if (mMessage != null) {
|
||||
// (Issue 3319) mController.setFlag(mAccount, mMessageReference.folderName, new String[] { mMessage.getUid() }, Flag.SEEN, false);
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getName(),
|
||||
new String[] { mMessage.getUid() }, Flag.SEEN, false);
|
||||
try {
|
||||
mMessage.setFlag(Flag.SEEN, false);
|
||||
// FIXME: This is a hack to mark our message object as unread. We can't call
|
||||
// Message.setFlag() because that would "adjust" the unread count twice.
|
||||
((LocalMessage)mMessage).setFlagInternal(Flag.SEEN, false);
|
||||
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
String subject = mMessage.getSubject();
|
||||
setTitle(subject);
|
||||
|
@ -3371,9 +3371,11 @@ public class MessagingController implements Runnable {
|
||||
localFolder = localStore.getFolder(account.getDraftsFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
String uid = localFolder.getMessageUidById(id);
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
deleteMessages(new Message[] { message }, null);
|
||||
if (uid != null) {
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
deleteMessages(new Message[] { message }, null);
|
||||
}
|
||||
}
|
||||
} catch (MessagingException me) {
|
||||
addErrorMessage(account, null, me);
|
||||
@ -3528,12 +3530,13 @@ public class MessagingController implements Runnable {
|
||||
putBackground("emptyTrash", listener, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Folder localFolder = null;
|
||||
LocalFolder localFolder = null;
|
||||
try {
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolder = localStore.getFolder(account.getTrashFolderName());
|
||||
localFolder = (LocalFolder) localStore.getFolder(account.getTrashFolderName());
|
||||
localFolder.open(OpenMode.READ_WRITE);
|
||||
localFolder.setFlags(new Flag[] { Flag.DELETED }, true);
|
||||
localFolder.resetUnreadAndFlaggedCounts();
|
||||
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.emptyTrashCompleted(account);
|
||||
|
@ -2725,7 +2725,7 @@ Log.d("ASH", "setting folder " + mName + " to localOnly = " + localOnly);
|
||||
setVisibleLimit(mAccount.getDisplayCount());
|
||||
}
|
||||
|
||||
private void resetUnreadAndFlaggedCounts() {
|
||||
public void resetUnreadAndFlaggedCounts() {
|
||||
try {
|
||||
int newUnread = 0;
|
||||
int newFlagged = 0;
|
||||
@ -2799,22 +2799,34 @@ Log.d("ASH", "setting folder " + mName + " to localOnly = " + localOnly);
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
Cursor attachmentsCursor = null;
|
||||
try {
|
||||
attachmentsCursor = db.query("attachments", new String[]
|
||||
{ "id" }, "message_id = ?", new String[]
|
||||
{ Long.toString(messageId) }, null, null, null);
|
||||
String accountUuid = mAccount.getUuid();
|
||||
Context context = mApplication;
|
||||
|
||||
// Get attachment IDs
|
||||
String[] whereArgs = new String[] { Long.toString(messageId) };
|
||||
attachmentsCursor = db.query("attachments", new String[] { "id" },
|
||||
"message_id = ?", whereArgs, null, null, null);
|
||||
|
||||
final File attachmentDirectory = StorageManager.getInstance(mApplication)
|
||||
.getAttachmentDirectory(uUid, database.getStorageProviderId());
|
||||
.getAttachmentDirectory(uUid, database.getStorageProviderId());
|
||||
|
||||
while (attachmentsCursor.moveToNext()) {
|
||||
long attachmentId = attachmentsCursor.getLong(0);
|
||||
String attachmentId = Long.toString(attachmentsCursor.getLong(0));
|
||||
try {
|
||||
File file = new File(attachmentDirectory, Long.toString(attachmentId));
|
||||
// Delete stored attachment
|
||||
File file = new File(attachmentDirectory, attachmentId);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
// Delete thumbnail file
|
||||
AttachmentProvider.deleteThumbnail(context, accountUuid,
|
||||
attachmentId);
|
||||
} catch (Exception e) { /* ignore */ }
|
||||
}
|
||||
|
||||
// Delete attachment metadata from the database
|
||||
db.delete("attachments", "message_id = ?", whereArgs);
|
||||
} finally {
|
||||
Utility.closeQuietly(attachmentsCursor);
|
||||
}
|
||||
|
@ -23,8 +23,12 @@ import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple ContentProvider that allows file access to Email's attachments.<br/>
|
||||
* Warning! We make heavy assumptions about the Uris used by the {@link LocalStore} for an {@link Account} here.
|
||||
* A simple ContentProvider that allows file access to attachments.
|
||||
*
|
||||
* <p>
|
||||
* Warning! We make heavy assumptions about the Uris used by the {@link LocalStore} for an
|
||||
* {@link Account} here.
|
||||
* </p>
|
||||
*/
|
||||
public class AttachmentProvider extends ContentProvider {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://com.fsck.k9.attachmentprovider");
|
||||
@ -33,6 +37,11 @@ public class AttachmentProvider extends ContentProvider {
|
||||
private static final String FORMAT_VIEW = "VIEW";
|
||||
private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
|
||||
|
||||
private static final String[] DEFAULT_PROJECTION = new String[] {
|
||||
AttachmentProviderColumns._ID,
|
||||
AttachmentProviderColumns.DATA,
|
||||
};
|
||||
|
||||
public static class AttachmentProviderColumns {
|
||||
public static final String _ID = "_id";
|
||||
public static final String DATA = "_data";
|
||||
@ -40,6 +49,7 @@ public class AttachmentProvider extends ContentProvider {
|
||||
public static final String SIZE = "_size";
|
||||
}
|
||||
|
||||
|
||||
public static Uri getAttachmentUri(Account account, long id) {
|
||||
return getAttachmentUri(account.getUuid(), id, true);
|
||||
}
|
||||
@ -66,6 +76,47 @@ public class AttachmentProvider extends ContentProvider {
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void clear(Context context) {
|
||||
/*
|
||||
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
|
||||
* on startup we'll clean up any .tmp files from the last run.
|
||||
*/
|
||||
File[] files = context.getCacheDir().listFiles();
|
||||
for (File file : files) {
|
||||
try {
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "Deleting file " + file.getCanonicalPath());
|
||||
}
|
||||
} catch (IOException ioe) { /* No need to log failure to log */ }
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the thumbnail of an attachment.
|
||||
*
|
||||
* @param context
|
||||
* The application context.
|
||||
* @param accountUuid
|
||||
* The UUID of the account the attachment belongs to.
|
||||
* @param attachmentId
|
||||
* The ID of the attachment the thumbnail was created for.
|
||||
*/
|
||||
public static void deleteThumbnail(Context context, String accountUuid, String attachmentId) {
|
||||
File file = getThumbnailFile(context, accountUuid, attachmentId);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static File getThumbnailFile(Context context, String accountUuid,
|
||||
String attachmentId) {
|
||||
String filename = "thmb_" + accountUuid + "_" + attachmentId + ".tmp";
|
||||
File dir = context.getCacheDir();
|
||||
return new File(dir, filename);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
/*
|
||||
@ -89,21 +140,6 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void clear(Context lContext) {
|
||||
/*
|
||||
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
|
||||
* on startup we'll clean up any .tmp files from the last run.
|
||||
*/
|
||||
File[] files = lContext.getCacheDir().listFiles();
|
||||
for (File file : files) {
|
||||
try {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Deleting file " + file.getCanonicalPath());
|
||||
} catch (IOException ioe) {} // No need to log failure to log
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
List<String> segments = uri.getPathSegments();
|
||||
@ -114,67 +150,24 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return getType(dbName, id, format);
|
||||
}
|
||||
|
||||
private String getType(String dbName, String id, String format) {
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
return "image/png";
|
||||
} else {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
try {
|
||||
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
|
||||
|
||||
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
||||
if (FORMAT_VIEW.equals(format)) {
|
||||
return MimeUtility.getMimeTypeForViewing(attachmentInfo.type, attachmentInfo.name);
|
||||
} else {
|
||||
// When accessing the "raw" message we deliver the original MIME type.
|
||||
return attachmentInfo.type;
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getFile(String dbName, String id)
|
||||
throws FileNotFoundException {
|
||||
try {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
final File attachmentsDir;
|
||||
attachmentsDir = StorageManager.getInstance(K9.app).getAttachmentDirectory(dbName,
|
||||
account.getLocalStorageProviderId());
|
||||
final File file = new File(attachmentsDir, id);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
Log.w(K9.LOG_TAG, null, e);
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
File file;
|
||||
|
||||
List<String> segments = uri.getPathSegments();
|
||||
String dbName = segments.get(0); // "/sdcard/..." is URL-encoded and makes up only 1 segment
|
||||
String id = segments.get(1);
|
||||
String accountUuid = segments.get(0);
|
||||
String attachmentId = segments.get(1);
|
||||
String format = segments.get(2);
|
||||
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
int width = Integer.parseInt(segments.get(3));
|
||||
int height = Integer.parseInt(segments.get(4));
|
||||
String filename = "thmb_" + dbName + "_" + id + ".tmp";
|
||||
int index = dbName.lastIndexOf('/');
|
||||
if (index >= 0) {
|
||||
filename = /*dbName.substring(0, index + 1) + */"thmb_" + dbName.substring(index + 1) + "_" + id + ".tmp";
|
||||
}
|
||||
File dir = getContext().getCacheDir();
|
||||
File file = new File(dir, filename);
|
||||
|
||||
file = getThumbnailFile(getContext(), accountUuid, attachmentId);
|
||||
if (!file.exists()) {
|
||||
String type = getType(dbName, id, FORMAT_VIEW);
|
||||
String type = getType(accountUuid, attachmentId, FORMAT_VIEW);
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(getFile(dbName, id));
|
||||
FileInputStream in = new FileInputStream(getFile(accountUuid, attachmentId));
|
||||
try {
|
||||
Bitmap thumbnail = createThumbnail(type, in);
|
||||
if (thumbnail != null) {
|
||||
@ -187,40 +180,24 @@ public class AttachmentProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try { in.close(); } catch (Throwable ignore) {}
|
||||
try { in.close(); } catch (Throwable ignore) { /* ignore */ }
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
} else {
|
||||
return ParcelFileDescriptor.open(
|
||||
getFile(dbName, id),
|
||||
ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
file = getFile(accountUuid, attachmentId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String arg1, String[] arg2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
if (projection == null) {
|
||||
projection =
|
||||
new String[] {
|
||||
AttachmentProviderColumns._ID,
|
||||
AttachmentProviderColumns.DATA,
|
||||
};
|
||||
}
|
||||
|
||||
String[] columnNames = (projection == null) ? DEFAULT_PROJECTION : projection;
|
||||
|
||||
List<String> segments = uri.getPathSegments();
|
||||
String dbName = segments.get(0);
|
||||
@ -232,7 +209,6 @@ public class AttachmentProvider extends ContentProvider {
|
||||
dbName = dbName.substring(0, dbName.length() - 3);
|
||||
}
|
||||
|
||||
//String format = segments.get(2);
|
||||
final AttachmentInfo attachmentInfo;
|
||||
try {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
@ -242,10 +218,10 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
MatrixCursor ret = new MatrixCursor(projection);
|
||||
Object[] values = new Object[projection.length];
|
||||
for (int i = 0, count = projection.length; i < count; i++) {
|
||||
String column = projection[i];
|
||||
MatrixCursor ret = new MatrixCursor(columnNames);
|
||||
Object[] values = new Object[columnNames.length];
|
||||
for (int i = 0, count = columnNames.length; i < count; i++) {
|
||||
String column = columnNames[i];
|
||||
if (AttachmentProviderColumns._ID.equals(column)) {
|
||||
values[i] = id;
|
||||
} else if (AttachmentProviderColumns.DATA.equals(column)) {
|
||||
@ -265,6 +241,56 @@ public class AttachmentProvider extends ContentProvider {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String arg1, String[] arg2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getType(String dbName, String id, String format) {
|
||||
String type;
|
||||
if (FORMAT_THUMBNAIL.equals(format)) {
|
||||
type = "image/png";
|
||||
} else {
|
||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
try {
|
||||
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
|
||||
|
||||
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
||||
if (FORMAT_VIEW.equals(format)) {
|
||||
type = MimeUtility.getMimeTypeForViewing(attachmentInfo.type, attachmentInfo.name);
|
||||
} else {
|
||||
// When accessing the "raw" message we deliver the original MIME type.
|
||||
type = attachmentInfo.type;
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to retrieve LocalStore for " + account, e);
|
||||
type = null;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private File getFile(String dbName, String id) throws FileNotFoundException {
|
||||
Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||
|
||||
File attachmentsDir = StorageManager.getInstance(K9.app).getAttachmentDirectory(dbName,
|
||||
account.getLocalStorageProviderId());
|
||||
|
||||
File file = new File(attachmentsDir, id);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private Bitmap createThumbnail(String type, InputStream data) {
|
||||
if (MimeUtility.mimeTypeMatches(type, "image/*")) {
|
||||
return createImageThumbnail(data);
|
||||
|
@ -39,6 +39,26 @@ import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
|
||||
public class AttachmentView extends FrameLayout {
|
||||
/**
|
||||
* Regular expression that represents characters we won't allow in file names.
|
||||
*
|
||||
* <p>
|
||||
* Allowed are:
|
||||
* <ul>
|
||||
* <li>word characters (letters, digits, and underscores): {@code \w}</li>
|
||||
* <li>spaces: {@code " "}</li>
|
||||
* <li>special characters: {@code !}, {@code #}, {@code $}, {@code %}, {@code &}, {@code '},
|
||||
* {@code (}, {@code )}, {@code -}, {@code @}, {@code ^}, {@code `}, <code>{</code>,
|
||||
* <code>}</code>, {@code ~}, {@code .}, {@code ,}</li>
|
||||
* </ul></p>
|
||||
*/
|
||||
private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
|
||||
|
||||
/**
|
||||
* Invalid characters in a file name are replaced by this character.
|
||||
*/
|
||||
private static final String REPLACEMENT_CHARACTER = "_";
|
||||
|
||||
|
||||
private Context mContext;
|
||||
public Button viewButton;
|
||||
@ -196,7 +216,8 @@ public class AttachmentView extends FrameLayout {
|
||||
*/
|
||||
public void writeFile(File directory) {
|
||||
try {
|
||||
File file = Utility.createUniqueFile(directory, name);
|
||||
String filename = sanitizeFilename(name);
|
||||
File file = Utility.createUniqueFile(directory, filename);
|
||||
Uri uri = AttachmentProvider.getAttachmentUri(mAccount, part.getAttachmentId());
|
||||
InputStream in = mContext.getContentResolver().openInputStream(uri);
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
@ -207,9 +228,25 @@ public class AttachmentView extends FrameLayout {
|
||||
attachmentSaved(file.toString());
|
||||
new MediaScannerNotifier(mContext, file);
|
||||
} catch (IOException ioe) {
|
||||
if (K9.DEBUG) {
|
||||
Log.e(K9.LOG_TAG, "Error saving attachment", ioe);
|
||||
}
|
||||
attachmentNotSaved();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace characters we don't allow in file names with a replacement character.
|
||||
*
|
||||
* @param filename
|
||||
* The original file name.
|
||||
*
|
||||
* @return The sanitized file name containing only allowed characters.
|
||||
*/
|
||||
private String sanitizeFilename(String filename) {
|
||||
return filename.replaceAll(INVALID_CHARACTERS, REPLACEMENT_CHARACTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* saves the file to the defaultpath setting in the config, or if the config
|
||||
* is not set => to the Environment
|
||||
|
Loading…
Reference in New Issue
Block a user