2008-11-01 17:32:06 -04:00
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
package com.fsck.k9.mail.store;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
import java.net.ConnectException;
|
|
|
|
import java.net.InetSocketAddress;
|
|
|
|
import java.net.Socket;
|
|
|
|
import java.net.SocketAddress;
|
|
|
|
import java.net.SocketException;
|
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.net.URLDecoder;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.CharBuffer;
|
|
|
|
import java.nio.charset.Charset;
|
|
|
|
import java.security.GeneralSecurityException;
|
|
|
|
import java.security.SecureRandom;
|
|
|
|
import java.security.Security;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.StringTokenizer;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
import javax.net.ssl.SSLContext;
|
|
|
|
import javax.net.ssl.SSLException;
|
|
|
|
import javax.net.ssl.TrustManager;
|
|
|
|
|
2010-03-06 19:30:40 -05:00
|
|
|
import android.content.Context;
|
|
|
|
import android.net.ConnectivityManager;
|
|
|
|
import android.net.NetworkInfo;
|
2010-05-16 20:30:32 -04:00
|
|
|
import android.os.PowerManager;
|
2008-11-01 17:32:06 -04:00
|
|
|
import android.util.Log;
|
2010-12-30 16:57:59 -05:00
|
|
|
|
|
|
|
import com.beetstra.jutf7.CharsetProvider;
|
2010-03-03 23:00:30 -05:00
|
|
|
import com.fsck.k9.Account;
|
2009-12-14 21:50:53 -05:00
|
|
|
import com.fsck.k9.K9;
|
2010-10-28 10:37:48 -04:00
|
|
|
import com.fsck.k9.R;
|
2010-05-19 14:17:06 -04:00
|
|
|
import com.fsck.k9.controller.MessageRetrievalListener;
|
|
|
|
import com.fsck.k9.helper.Utility;
|
2010-05-16 20:30:32 -04:00
|
|
|
import com.fsck.k9.helper.power.TracingPowerManager;
|
|
|
|
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
2011-04-07 11:11:32 -04:00
|
|
|
import com.fsck.k9.mail.Authentication;
|
2010-12-30 16:57:59 -05:00
|
|
|
import com.fsck.k9.mail.AuthenticationFailedException;
|
|
|
|
import com.fsck.k9.mail.Body;
|
|
|
|
import com.fsck.k9.mail.CertificateValidationException;
|
|
|
|
import com.fsck.k9.mail.FetchProfile;
|
|
|
|
import com.fsck.k9.mail.Flag;
|
|
|
|
import com.fsck.k9.mail.Folder;
|
|
|
|
import com.fsck.k9.mail.Message;
|
|
|
|
import com.fsck.k9.mail.MessagingException;
|
|
|
|
import com.fsck.k9.mail.Part;
|
|
|
|
import com.fsck.k9.mail.PushReceiver;
|
|
|
|
import com.fsck.k9.mail.Pusher;
|
|
|
|
import com.fsck.k9.mail.Store;
|
2010-05-19 14:17:06 -04:00
|
|
|
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
|
|
|
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
|
|
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
2010-12-30 16:57:59 -05:00
|
|
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
|
|
|
import com.fsck.k9.mail.internet.MimeHeader;
|
|
|
|
import com.fsck.k9.mail.internet.MimeMessage;
|
|
|
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
|
|
|
import com.fsck.k9.mail.internet.MimeUtility;
|
2009-12-14 21:50:53 -05:00
|
|
|
import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
|
|
|
|
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
2010-12-30 16:57:59 -05:00
|
|
|
import com.fsck.k9.mail.transport.imap.ImapSettings;
|
2010-03-06 19:30:40 -05:00
|
|
|
import com.jcraft.jzlib.JZlib;
|
|
|
|
import com.jcraft.jzlib.ZOutputStream;
|
2011-02-12 04:01:05 -05:00
|
|
|
import java.util.zip.Inflater;
|
|
|
|
import java.util.zip.InflaterInputStream;
|
2009-12-09 22:16:42 -05:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
/**
|
|
|
|
* <pre>
|
|
|
|
* TODO Need to start keeping track of UIDVALIDITY
|
|
|
|
* TODO Need a default response handler for things like folder updates
|
|
|
|
* </pre>
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class ImapStore extends Store {
|
2008-11-01 17:32:06 -04:00
|
|
|
public static final int CONNECTION_SECURITY_NONE = 0;
|
|
|
|
public static final int CONNECTION_SECURITY_TLS_OPTIONAL = 1;
|
|
|
|
public static final int CONNECTION_SECURITY_TLS_REQUIRED = 2;
|
|
|
|
public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
|
|
|
|
public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
public enum AuthType { PLAIN, CRAM_MD5 }
|
2009-12-20 00:41:43 -05:00
|
|
|
|
2010-05-09 11:27:41 -04:00
|
|
|
private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000;
|
2010-04-14 23:17:25 -04:00
|
|
|
private static final int IDLE_FAILURE_COUNT_LIMIT = 10;
|
|
|
|
private static int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes
|
2010-04-29 00:59:14 -04:00
|
|
|
private static int NORMAL_DELAY_TIME = 5000;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
private static int FETCH_WINDOW_SIZE = 100;
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.SEEN };
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2009-11-28 09:51:44 -05:00
|
|
|
private static final String CAPABILITY_IDLE = "IDLE";
|
|
|
|
private static final String COMMAND_IDLE = "IDLE";
|
|
|
|
private static final String CAPABILITY_NAMESPACE = "NAMESPACE";
|
|
|
|
private static final String COMMAND_NAMESPACE = "NAMESPACE";
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-02-27 12:34:38 -05:00
|
|
|
private static final String CAPABILITY_CAPABILITY = "CAPABILITY";
|
|
|
|
private static final String COMMAND_CAPABILITY = "CAPABILITY";
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-03-06 19:30:40 -05:00
|
|
|
private static final String CAPABILITY_COMPRESS_DEFLATE = "COMPRESS=DEFLATE";
|
|
|
|
private static final String COMMAND_COMPRESS_DEFLATE = "COMPRESS DEFLATE";
|
2010-02-27 12:34:38 -05:00
|
|
|
|
2010-08-07 11:10:07 -04:00
|
|
|
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
|
|
|
|
|
|
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
private String mHost;
|
|
|
|
private int mPort;
|
|
|
|
private String mUsername;
|
|
|
|
private String mPassword;
|
|
|
|
private int mConnectionSecurity;
|
2009-12-20 00:15:20 -05:00
|
|
|
private AuthType mAuthType;
|
2009-11-28 09:51:44 -05:00
|
|
|
private volatile String mPathPrefix;
|
|
|
|
private volatile String mCombinedPrefix = null;
|
2010-05-19 22:33:44 -04:00
|
|
|
private volatile String mPathDelimeter = null;
|
2010-07-06 06:29:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public class StoreImapSettings implements ImapSettings {
|
2010-12-30 16:57:59 -05:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getHost() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mHost;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getPort() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mPort;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getConnectionSecurity() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mConnectionSecurity;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public AuthType getAuthType() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mAuthType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUsername() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mUsername;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getPassword() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mPassword;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean useCompression(final int type) {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mAccount.useCompression(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getPathPrefix() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mPathPrefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setPathPrefix(String prefix) {
|
2010-12-30 16:57:59 -05:00
|
|
|
mPathPrefix = prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getPathDelimeter() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mPathDelimeter;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setPathDelimeter(String delimeter) {
|
2010-12-30 16:57:59 -05:00
|
|
|
mPathDelimeter = delimeter;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getCombinedPrefix() {
|
2010-12-30 16:57:59 -05:00
|
|
|
return mCombinedPrefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setCombinedPrefix(String prefix) {
|
2010-12-30 16:57:59 -05:00
|
|
|
mCombinedPrefix = prefix;
|
|
|
|
}
|
2011-01-05 19:39:09 -05:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
}
|
|
|
|
|
2010-06-11 08:56:01 -04:00
|
|
|
private static final SimpleDateFormat RFC3501_DATE = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
private LinkedList<ImapConnection> mConnections =
|
2009-11-24 19:40:29 -05:00
|
|
|
new LinkedList<ImapConnection>();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Charset used for converting folder names to and from UTF-7 as defined by RFC 3501.
|
|
|
|
*/
|
|
|
|
private Charset mModifiedUtf7Charset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cache of ImapFolder objects. ImapFolders are attached to a given folder on the server
|
|
|
|
* and as long as their associated connection remains open they are reusable between
|
|
|
|
* requests. This cache lets us make sure we always reuse, if possible, for a given
|
|
|
|
* folder name.
|
|
|
|
*/
|
|
|
|
private HashMap<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
|
|
|
|
|
|
|
|
/**
|
2009-12-20 00:15:20 -05:00
|
|
|
* imap://auth:user:password@server:port CONNECTION_SECURITY_NONE
|
|
|
|
* imap+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
|
|
|
* imap+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
|
|
|
* imap+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
|
|
|
* imap+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
2010-03-06 19:30:40 -05:00
|
|
|
*
|
|
|
|
* @param _uri
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapStore(Account account) throws MessagingException {
|
2010-03-03 23:00:30 -05:00
|
|
|
super(account);
|
2008-11-01 17:32:06 -04:00
|
|
|
URI uri;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-03-03 23:00:30 -05:00
|
|
|
uri = new URI(mAccount.getStoreUri());
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (URISyntaxException use) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException("Invalid ImapStore URI", use);
|
|
|
|
}
|
|
|
|
|
|
|
|
String scheme = uri.getScheme();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (scheme.equals("imap")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
|
|
|
mPort = 143;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (scheme.equals("imap+tls")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
|
|
|
mPort = 143;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (scheme.equals("imap+tls+")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
|
|
|
mPort = 143;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (scheme.equals("imap+ssl+")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
|
|
|
mPort = 993;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (scheme.equals("imap+ssl")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
|
|
|
mPort = 993;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException("Unsupported protocol");
|
|
|
|
}
|
|
|
|
|
|
|
|
mHost = uri.getHost();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (uri.getPort() != -1) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mPort = uri.getPort();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (uri.getUserInfo() != null) {
|
|
|
|
try {
|
2010-02-01 21:06:29 -05:00
|
|
|
String[] userInfoParts = uri.getUserInfo().split(":");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (userInfoParts.length == 2) {
|
2010-02-01 21:06:29 -05:00
|
|
|
mAuthType = AuthType.PLAIN;
|
|
|
|
mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
|
|
|
mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-02-01 21:06:29 -05:00
|
|
|
mAuthType = AuthType.valueOf(userInfoParts[0]);
|
|
|
|
mUsername = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
|
|
|
mPassword = URLDecoder.decode(userInfoParts[2], "UTF-8");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (UnsupportedEncodingException enc) {
|
2010-02-01 21:06:29 -05:00
|
|
|
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
|
|
|
Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
|
2009-12-20 01:14:04 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if ((uri.getPath() != null) && (uri.getPath().length() > 0)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mPathPrefix = uri.getPath().substring(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mPathPrefix != null && mPathPrefix.trim().length() == 0) {
|
2009-11-28 09:51:44 -05:00
|
|
|
mPathPrefix = null;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
mModifiedUtf7Charset = new CharsetProvider().charsetForName("X-RFC-3501");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Folder getFolder(String name) {
|
2008-11-01 17:32:06 -04:00
|
|
|
ImapFolder folder;
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (mFolderCache) {
|
2008-11-01 17:32:06 -04:00
|
|
|
folder = mFolderCache.get(name);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (folder == null) {
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
folder = new ImapFolder(this, name);
|
2008-11-01 17:32:06 -04:00
|
|
|
mFolderCache.put(name, folder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String getCombinedPrefix() {
|
|
|
|
if (mCombinedPrefix == null) {
|
|
|
|
if (mPathPrefix != null) {
|
2009-11-28 09:51:44 -05:00
|
|
|
String tmpPrefix = mPathPrefix.trim();
|
|
|
|
String tmpDelim = (mPathDelimeter != null ? mPathDelimeter.trim() : "");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (tmpPrefix.endsWith(tmpDelim)) {
|
2009-11-28 09:51:44 -05:00
|
|
|
mCombinedPrefix = tmpPrefix;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (tmpPrefix.length() > 0) {
|
2009-11-28 09:51:44 -05:00
|
|
|
mCombinedPrefix = tmpPrefix + tmpDelim;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-28 09:51:44 -05:00
|
|
|
mCombinedPrefix = "";
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-28 09:51:44 -05:00
|
|
|
mCombinedPrefix = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mCombinedPrefix;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
ImapConnection connection = getConnection();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
List <? extends Folder > allFolders = listFolders(connection, false);
|
|
|
|
if (forceListAll || !mAccount.subscribedFoldersOnly()) {
|
2010-05-30 12:56:50 -04:00
|
|
|
return allFolders;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-30 12:56:50 -04:00
|
|
|
List<Folder> resultFolders = new LinkedList<Folder>();
|
|
|
|
HashSet<String> subscribedFolderNames = new HashSet<String>();
|
2011-02-06 17:09:48 -05:00
|
|
|
List <? extends Folder > subscribedFolders = listFolders(connection, true);
|
|
|
|
for (Folder subscribedFolder : subscribedFolders) {
|
2010-05-30 12:56:50 -04:00
|
|
|
subscribedFolderNames.add(subscribedFolder.getName());
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Folder folder : allFolders) {
|
|
|
|
if (subscribedFolderNames.contains(folder.getName())) {
|
2010-05-30 12:56:50 -04:00
|
|
|
resultFolders.add(folder);
|
2010-07-06 06:29:26 -04:00
|
|
|
}
|
2010-05-30 12:56:50 -04:00
|
|
|
}
|
|
|
|
return resultFolders;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-05-30 12:56:50 -04:00
|
|
|
connection.close();
|
|
|
|
throw new MessagingException("Unable to get folder list.", ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2010-05-30 12:56:50 -04:00
|
|
|
connection.close();
|
|
|
|
throw new MessagingException("Unable to get folder list.", me);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2010-05-30 12:56:50 -04:00
|
|
|
releaseConnection(connection);
|
|
|
|
}
|
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private List <? extends Folder > listFolders(ImapConnection connection, boolean LSUB) throws IOException, MessagingException {
|
2010-07-06 06:29:26 -04:00
|
|
|
String commandResponse = LSUB ? "LSUB" : "LIST";
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2010-07-06 06:29:26 -04:00
|
|
|
LinkedList<Folder> folders = new LinkedList<Folder>();
|
2008-11-03 01:12:04 -05:00
|
|
|
|
2010-07-06 06:29:26 -04:00
|
|
|
List<ImapResponse> responses =
|
2011-01-05 19:39:09 -05:00
|
|
|
connection.executeSimpleCommand(String.format(commandResponse + " \"\" %s",
|
|
|
|
encodeString(getCombinedPrefix() + "*")));
|
2010-07-06 06:29:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : responses) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), commandResponse)) {
|
2010-07-06 06:29:26 -04:00
|
|
|
boolean includeFolder = true;
|
|
|
|
String folder = decodeFolderName(response.getString(3));
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mPathDelimeter == null) {
|
2010-07-06 06:29:26 -04:00
|
|
|
mPathDelimeter = response.getString(2);
|
|
|
|
mCombinedPrefix = null;
|
|
|
|
}
|
2008-12-02 23:14:39 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (folder.equalsIgnoreCase(K9.INBOX)) {
|
2010-07-06 06:29:26 -04:00
|
|
|
continue;
|
2011-02-19 22:47:28 -05:00
|
|
|
} else if (folder.equalsIgnoreCase(K9.OUTBOX)) {
|
|
|
|
/*
|
|
|
|
* There is a folder on the server with the same name as our local
|
|
|
|
* outbox. Until we have a good plan to deal with this situation
|
|
|
|
* we simply ignore the folder on the server.
|
|
|
|
*/
|
|
|
|
continue;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-12-02 23:14:39 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (getCombinedPrefix().length() > 0) {
|
|
|
|
if (folder.length() >= getCombinedPrefix().length()) {
|
2010-07-06 06:29:26 -04:00
|
|
|
folder = folder.substring(getCombinedPrefix().length());
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!decodeFolderName(response.getString(3)).equalsIgnoreCase(getCombinedPrefix() + folder)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
includeFolder = false;
|
|
|
|
}
|
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
ImapList attributes = response.getList(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = attributes.size(); i < count; i++) {
|
2010-07-06 06:29:26 -04:00
|
|
|
String attribute = attributes.getString(i);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (attribute.equalsIgnoreCase("\\NoSelect")) {
|
2010-07-06 06:29:26 -04:00
|
|
|
includeFolder = false;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (includeFolder) {
|
2010-07-06 06:29:26 -04:00
|
|
|
folders.add(getFolder(folder));
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
}
|
|
|
|
folders.add(getFolder("INBOX"));
|
|
|
|
return folders;
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void checkSettings() throws MessagingException {
|
|
|
|
try {
|
2010-12-30 16:57:59 -05:00
|
|
|
ImapConnection connection = new ImapConnection(new StoreImapSettings());
|
2008-11-01 17:32:06 -04:00
|
|
|
connection.open();
|
|
|
|
connection.close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-10-28 10:37:48 -04:00
|
|
|
throw new MessagingException(K9.app.getString(R.string.error_unable_to_connect), ioe);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a connection if one is available for reuse, or creates a new one if not.
|
|
|
|
* @return
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private ImapConnection getConnection() throws MessagingException {
|
|
|
|
synchronized (mConnections) {
|
2008-11-01 17:32:06 -04:00
|
|
|
ImapConnection connection = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
while ((connection = mConnections.poll()) != null) {
|
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection.executeSimpleCommand("NOOP");
|
|
|
|
break;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection.close();
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (connection == null) {
|
2010-12-30 16:57:59 -05:00
|
|
|
connection = new ImapConnection(new StoreImapSettings());
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void releaseConnection(ImapConnection connection) {
|
|
|
|
if (connection != null && connection.isOpen()) {
|
|
|
|
synchronized (mConnections) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mConnections.offer(connection);
|
|
|
|
}
|
2009-11-22 12:01:04 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-01-05 19:39:09 -05:00
|
|
|
/**
|
|
|
|
* Encode a string to be able to use it in an IMAP command.
|
|
|
|
*
|
|
|
|
* "A quoted string is a sequence of zero or more 7-bit characters,
|
|
|
|
* excluding CR and LF, with double quote (<">) characters at each
|
2011-01-06 11:55:34 -05:00
|
|
|
* end." - Section 4.3, RFC 3501
|
2011-01-05 19:39:09 -05:00
|
|
|
*
|
|
|
|
* Double quotes and backslash are escaped by prepending a backslash.
|
|
|
|
*
|
|
|
|
* @param str
|
|
|
|
* The input string (only 7-bit characters allowed).
|
|
|
|
* @return
|
|
|
|
* The string encoded as quoted (IMAP) string.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private static String encodeString(String str) {
|
2011-01-05 19:39:09 -05:00
|
|
|
return "\"" + str.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String encodeFolderName(String name) {
|
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
ByteBuffer bb = mModifiedUtf7Charset.encode(name);
|
|
|
|
byte[] b = new byte[bb.limit()];
|
|
|
|
bb.get(b);
|
|
|
|
return new String(b, "US-ASCII");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (UnsupportedEncodingException uee) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* The only thing that can throw this is getBytes("US-ASCII") and if US-ASCII doesn't
|
|
|
|
* exist we're totally screwed.
|
|
|
|
*/
|
2008-11-06 01:55:56 -05:00
|
|
|
throw new RuntimeException("Unable to encode folder name: " + name, uee);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String decodeFolderName(String name) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* Convert the encoded name to US-ASCII, then pass it through the modified UTF-7
|
|
|
|
* decoder and return the Unicode String.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
byte[] encoded = name.getBytes("US-ASCII");
|
|
|
|
CharBuffer cb = mModifiedUtf7Charset.decode(ByteBuffer.wrap(encoded));
|
|
|
|
return cb.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (UnsupportedEncodingException uee) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* The only thing that can throw this is getBytes("US-ASCII") and if US-ASCII doesn't
|
|
|
|
* exist we're totally screwed.
|
|
|
|
*/
|
|
|
|
throw new RuntimeException("Unable to decode folder name: " + name, uee);
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-03-05 02:32:45 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isMoveCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-03-05 02:32:45 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-03-05 02:32:45 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isCopyCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-03-05 02:32:45 -05:00
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isPushCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isExpungeCapable() {
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
return true;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
class ImapFolder extends Folder {
|
2008-11-01 17:32:06 -04:00
|
|
|
private String mName;
|
2010-05-25 23:24:33 -04:00
|
|
|
protected volatile int mMessageCount = -1;
|
|
|
|
protected volatile int uidNext = -1;
|
|
|
|
protected volatile ImapConnection mConnection;
|
2008-11-01 17:32:06 -04:00
|
|
|
private OpenMode mMode;
|
2010-05-25 23:24:33 -04:00
|
|
|
private volatile boolean mExists;
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
private ImapStore store = null;
|
2010-05-15 15:35:07 -04:00
|
|
|
Map<Integer, String> msgSeqUidMap = new ConcurrentHashMap<Integer, String>();
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapFolder(ImapStore nStore, String name) {
|
2010-03-03 23:00:30 -05:00
|
|
|
super(nStore.getAccount());
|
2009-11-24 19:40:29 -05:00
|
|
|
store = nStore;
|
|
|
|
this.mName = name;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getPrefixedName() throws MessagingException {
|
2009-11-24 19:40:29 -05:00
|
|
|
String prefixedName = "";
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!K9.INBOX.equalsIgnoreCase(mName)) {
|
2010-05-22 13:34:16 -04:00
|
|
|
ImapConnection connection = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (this) {
|
|
|
|
if (mConnection == null) {
|
2010-05-22 13:34:16 -04:00
|
|
|
connection = getConnection();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-22 13:34:16 -04:00
|
|
|
connection = mConnection;
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-22 13:34:16 -04:00
|
|
|
connection.open();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-05-22 13:34:16 -04:00
|
|
|
throw new MessagingException("Unable to get IMAP prefix", ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
|
|
|
if (mConnection == null) {
|
2010-05-22 13:34:16 -04:00
|
|
|
releaseConnection(connection);
|
|
|
|
}
|
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
prefixedName = getCombinedPrefix();
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
prefixedName += mName;
|
|
|
|
return prefixedName;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected List<ImapResponse> executeSimpleCommand(String command) throws MessagingException, IOException {
|
2009-11-24 19:40:29 -05:00
|
|
|
return handleUntaggedResponses(mConnection.executeSimpleCommand(command));
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected List<ImapResponse> executeSimpleCommand(String command, boolean sensitve, UntaggedHandler untaggedHandler) throws MessagingException, IOException {
|
2009-11-24 19:40:29 -05:00
|
|
|
return handleUntaggedResponses(mConnection.executeSimpleCommand(command, sensitve, untaggedHandler));
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void open(OpenMode mode) throws MessagingException {
|
2009-11-24 19:40:29 -05:00
|
|
|
internalOpen(mode);
|
2009-11-26 00:10:12 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mMessageCount == -1) {
|
2009-11-26 00:10:12 -05:00
|
|
|
throw new MessagingException(
|
|
|
|
"Did not find message count during open");
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public List<ImapResponse> internalOpen(OpenMode mode) throws MessagingException {
|
|
|
|
if (isOpen() && mMode == mode) {
|
2009-11-24 19:40:29 -05:00
|
|
|
// Make sure the connection is valid. If it's not we'll close it down and continue
|
|
|
|
// on to get a new one.
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
List<ImapResponse> responses = executeSimpleCommand("NOOP");
|
|
|
|
return responses;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
2010-02-13 12:02:27 -05:00
|
|
|
releaseConnection(mConnection);
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (this) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mConnection = getConnection();
|
|
|
|
}
|
|
|
|
// * FLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk
|
|
|
|
// $MDNSent)
|
|
|
|
// * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft
|
|
|
|
// NonJunk $MDNSent \*)] Flags permitted.
|
|
|
|
// * 23 EXISTS
|
|
|
|
// * 0 RECENT
|
|
|
|
// * OK [UIDVALIDITY 1125022061] UIDs valid
|
|
|
|
// * OK [UIDNEXT 57576] Predicted next UID
|
|
|
|
// 2 OK [READ-WRITE] Select completed.
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-15 15:35:07 -04:00
|
|
|
msgSeqUidMap.clear();
|
2011-01-05 19:39:09 -05:00
|
|
|
String command = String.format((mode == OpenMode.READ_WRITE ? "SELECT" : "EXAMINE") + " %s",
|
|
|
|
encodeString(encodeFolderName(getPrefixedName())));
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
List<ImapResponse> responses = executeSimpleCommand(command);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the command succeeds we expect the folder has been opened read-write
|
|
|
|
* unless we are notified otherwise in the responses.
|
|
|
|
*/
|
2010-02-13 12:02:27 -05:00
|
|
|
mMode = mode;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : responses) {
|
|
|
|
if (response.mTag != null && response.size() >= 2) {
|
2009-11-24 19:40:29 -05:00
|
|
|
Object bracketedObj = response.get(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bracketedObj instanceof ImapList) {
|
2009-11-24 19:40:29 -05:00
|
|
|
ImapList bracketed = (ImapList)bracketedObj;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bracketed.size() > 0) {
|
2009-11-24 19:40:29 -05:00
|
|
|
Object keyObj = bracketed.get(0);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (keyObj instanceof String) {
|
2009-11-24 19:40:29 -05:00
|
|
|
String key = (String)keyObj;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if ("READ-ONLY".equalsIgnoreCase(key)) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mMode = OpenMode.READ_ONLY;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if ("READ-WRITE".equalsIgnoreCase(key)) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mMode = OpenMode.READ_WRITE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mExists = true;
|
2009-11-26 00:10:12 -05:00
|
|
|
return responses;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to open connection for " + getLogId(), me);
|
2009-11-24 19:40:29 -05:00
|
|
|
throw me;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isOpen() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mConnection != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public OpenMode getMode() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mMode;
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void close() {
|
|
|
|
if (mMessageCount != -1) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mMessageCount = -1;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!isOpen()) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return;
|
|
|
|
}
|
2010-01-02 20:50:51 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (this) {
|
2008-11-01 17:32:06 -04:00
|
|
|
releaseConnection(mConnection);
|
|
|
|
mConnection = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getName() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mName;
|
|
|
|
}
|
2010-01-16 23:20:39 -05:00
|
|
|
|
2011-01-05 19:39:09 -05:00
|
|
|
/**
|
|
|
|
* Check if a given folder exists on the server.
|
|
|
|
*
|
|
|
|
* @param folderName
|
|
|
|
* The name of the folder encoded as quoted string.
|
|
|
|
* See {@link ImapStore#encodeString}
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* {@code True}, if the folder exists. {@code False}, otherwise.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private boolean exists(String folderName) throws MessagingException {
|
|
|
|
try {
|
2010-01-16 11:22:20 -05:00
|
|
|
// Since we don't care about RECENT, we'll use that for the check, because we're checking
|
|
|
|
// a folder other than ourself, and don't want any untagged responses to cause a change
|
|
|
|
// in our own fields
|
2011-01-05 19:39:09 -05:00
|
|
|
mConnection.executeSimpleCommand(String.format("STATUS %s (RECENT)", folderName));
|
2010-01-16 11:22:20 -05:00
|
|
|
return true;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2010-01-16 11:22:20 -05:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2010-01-16 11:22:20 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean exists() throws MessagingException {
|
|
|
|
if (mExists) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* This method needs to operate in the unselected mode as well as the selected mode
|
|
|
|
* so we must get the connection ourselves if it's not there. We are specifically
|
|
|
|
* not calling checkOpen() since we don't care if the folder is open.
|
|
|
|
*/
|
|
|
|
ImapConnection connection = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (this) {
|
|
|
|
if (mConnection == null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection = getConnection();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection = mConnection;
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2011-01-05 19:39:09 -05:00
|
|
|
connection.executeSimpleCommand(String.format("STATUS %s (UIDVALIDITY)",
|
|
|
|
encodeString(encodeFolderName(getPrefixedName()))));
|
2008-11-01 17:32:06 -04:00
|
|
|
mExists = true;
|
|
|
|
return true;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return false;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(connection, ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
|
|
|
if (mConnection == null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
releaseConnection(connection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean create(FolderType type) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* This method needs to operate in the unselected mode as well as the selected mode
|
|
|
|
* so we must get the connection ourselves if it's not there. We are specifically
|
|
|
|
* not calling checkOpen() since we don't care if the folder is open.
|
|
|
|
*/
|
|
|
|
ImapConnection connection = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (this) {
|
|
|
|
if (mConnection == null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection = getConnection();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
connection = mConnection;
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2011-01-05 19:39:09 -05:00
|
|
|
connection.executeSimpleCommand(String.format("CREATE %s",
|
|
|
|
encodeString(encodeFolderName(getPrefixedName()))));
|
2008-11-01 17:32:06 -04:00
|
|
|
return true;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return false;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
|
|
|
if (mConnection == null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
releaseConnection(connection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
|
|
|
if (!(folder instanceof ImapFolder)) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw new MessagingException("ImapFolder.copyMessages passed non-ImapFolder");
|
|
|
|
}
|
2009-12-15 12:40:04 -05:00
|
|
|
|
2009-12-20 00:41:43 -05:00
|
|
|
if (messages.length == 0)
|
2009-12-15 12:40:04 -05:00
|
|
|
return;
|
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
ImapFolder iFolder = (ImapFolder)folder;
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
|
|
|
String[] uids = new String[messages.length];
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-11-01 17:32:06 -04:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2011-01-05 19:39:09 -05:00
|
|
|
String remoteDestName = encodeString(encodeFolderName(iFolder.getPrefixedName()));
|
2010-01-16 23:20:39 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!exists(remoteDestName)) {
|
2010-01-16 11:22:20 -05:00
|
|
|
/*
|
|
|
|
* If the remote trash folder doesn't exist we try to create it.
|
|
|
|
*/
|
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "IMAPMessage.copyMessages: attempting to create remote '" + remoteDestName + "' folder for " + getLogId());
|
|
|
|
iFolder.create(FolderType.HOLDS_MESSAGES);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (exists(remoteDestName)) {
|
2011-01-05 19:39:09 -05:00
|
|
|
executeSimpleCommand(String.format("UID COPY %s %s",
|
2010-01-16 11:22:20 -05:00
|
|
|
Utility.combine(uids, ','),
|
2011-01-05 19:39:09 -05:00
|
|
|
remoteDestName));
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-01-17 19:11:02 -05:00
|
|
|
throw new MessagingException("IMAPMessage.copyMessages: remote destination folder " + folder.getName()
|
|
|
|
+ " does not exist and could not be created for " + getLogId()
|
2010-01-16 11:22:20 -05:00
|
|
|
, true);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-03-05 02:32:45 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
2009-12-20 00:41:43 -05:00
|
|
|
if (messages.length == 0)
|
2009-12-15 12:40:04 -05:00
|
|
|
return;
|
2009-11-24 19:40:29 -05:00
|
|
|
copyMessages(messages, folder);
|
|
|
|
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
2009-03-05 02:32:45 -05:00
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(Message[] messages, String trashFolderName) throws MessagingException {
|
2009-12-20 00:41:43 -05:00
|
|
|
if (messages.length == 0)
|
2009-12-15 12:40:04 -05:00
|
|
|
return;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (trashFolderName == null || getName().equalsIgnoreCase(trashFolderName)) {
|
2009-11-29 13:07:34 -05:00
|
|
|
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-29 13:07:34 -05:00
|
|
|
ImapFolder remoteTrashFolder = (ImapFolder)getStore().getFolder(trashFolderName);
|
2011-01-05 19:39:09 -05:00
|
|
|
String remoteTrashName = encodeString(encodeFolderName(remoteTrashFolder.getPrefixedName()));
|
2010-01-17 19:11:02 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!exists(remoteTrashName)) {
|
2009-11-29 13:07:34 -05:00
|
|
|
/*
|
|
|
|
* If the remote trash folder doesn't exist we try to create it.
|
|
|
|
*/
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "IMAPMessage.delete: attempting to create remote '" + trashFolderName + "' folder for " + getLogId());
|
2009-11-29 13:07:34 -05:00
|
|
|
remoteTrashFolder.create(FolderType.HOLDS_MESSAGES);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (exists(remoteTrashName)) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "IMAPMessage.delete: copying remote " + messages.length + " messages to '" + trashFolderName + "' for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-29 13:07:34 -05:00
|
|
|
moveMessages(messages, remoteTrashFolder);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-29 13:07:34 -05:00
|
|
|
throw new MessagingException("IMAPMessage.delete: remote Trash folder " + trashFolderName + " does not exist and could not be created for " + getLogId()
|
|
|
|
, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getMessageCount() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mMessageCount;
|
|
|
|
}
|
|
|
|
|
2010-07-11 08:31:34 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private int getRemoteMessageCount(String criteria) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-02-13 12:02:27 -05:00
|
|
|
int count = 0;
|
2010-07-11 11:39:26 -04:00
|
|
|
int start = 1;
|
2010-07-11 08:31:22 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
List<ImapResponse> responses = executeSimpleCommand(String.format("SEARCH %d:* " + criteria, start));
|
|
|
|
for (ImapResponse response : responses) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
|
2010-09-09 19:52:06 -04:00
|
|
|
count += response.size() - 1;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
2010-02-13 12:02:27 -05:00
|
|
|
return count;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
2010-07-11 08:31:34 -04:00
|
|
|
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-04-16 10:33:54 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getUnreadMessageCount() throws MessagingException {
|
2010-07-11 11:41:20 -04:00
|
|
|
return getRemoteMessageCount("UNSEEN NOT DELETED");
|
2010-07-11 08:31:34 -04:00
|
|
|
}
|
2010-07-11 08:31:22 -04:00
|
|
|
|
2010-07-11 08:31:34 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getFlaggedMessageCount() throws MessagingException {
|
2010-07-11 11:41:20 -04:00
|
|
|
return getRemoteMessageCount("FLAGGED NOT DELETED");
|
2010-04-16 10:33:54 -04:00
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected int getHighestUid() {
|
|
|
|
try {
|
|
|
|
ImapSearcher searcher = new ImapSearcher() {
|
|
|
|
public List<ImapResponse> search() throws IOException, MessagingException {
|
2011-03-03 21:36:52 -05:00
|
|
|
return executeSimpleCommand("UID SEARCH *:*");
|
2010-04-27 00:06:26 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Message[] messages = search(searcher, null);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messages.length > 0) {
|
2010-04-27 00:06:26 -04:00
|
|
|
return Integer.parseInt(messages[0].getUid());
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-04-27 00:06:26 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to find highest UID in folder " + getName(), e);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(boolean recurse) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new Error("ImapStore.delete() not yet implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message getMessage(String uid) throws MessagingException {
|
2009-10-21 20:41:06 -04:00
|
|
|
return new ImapMessage(uid, this);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
@Override
|
2010-05-30 17:20:47 -04:00
|
|
|
public Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2010-05-30 17:20:47 -04:00
|
|
|
return getMessages(start, end, earliestDate, false, listener);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-05-30 17:20:47 -04:00
|
|
|
protected Message[] getMessages(final int start, final int end, Date earliestDate, final boolean includeDeleted, final MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
if (start < 1 || end < 1 || end < start) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException(
|
2009-11-24 19:40:29 -05:00
|
|
|
String.format("Invalid message set %d %d",
|
|
|
|
start, end));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-05-30 17:20:47 -04:00
|
|
|
final StringBuilder dateSearchString = new StringBuilder();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (earliestDate != null) {
|
2010-06-02 10:25:04 -04:00
|
|
|
dateSearchString.append(" SINCE ");
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (RFC3501_DATE) {
|
2010-05-30 17:20:47 -04:00
|
|
|
dateSearchString.append(RFC3501_DATE.format(earliestDate));
|
|
|
|
}
|
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapSearcher searcher = new ImapSearcher() {
|
|
|
|
public List<ImapResponse> search() throws IOException, MessagingException {
|
2010-06-02 10:25:04 -04:00
|
|
|
return executeSimpleCommand(String.format("UID SEARCH %d:%d%s" + (includeDeleted ? "" : " NOT DELETED"), start, end, dateSearchString));
|
2009-11-26 00:10:12 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
return search(searcher, listener);
|
|
|
|
|
|
|
|
}
|
|
|
|
protected Message[] getMessages(final List<Integer> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
ImapSearcher searcher = new ImapSearcher() {
|
|
|
|
public List<ImapResponse> search() throws IOException, MessagingException {
|
2009-11-26 00:10:12 -05:00
|
|
|
return executeSimpleCommand(String.format("UID SEARCH %s" + (includeDeleted ? "" : " NOT DELETED"), Utility.combine(mesgSeqs.toArray(), ',')));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return search(searcher, listener);
|
|
|
|
}
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2010-05-15 15:35:07 -04:00
|
|
|
protected Message[] getMessagesFromUids(final List<String> mesgUids, final boolean includeDeleted, final MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
ImapSearcher searcher = new ImapSearcher() {
|
|
|
|
public List<ImapResponse> search() throws IOException, MessagingException {
|
2010-05-15 15:35:07 -04:00
|
|
|
return executeSimpleCommand(String.format("UID SEARCH UID %s" + (includeDeleted ? "" : " NOT DELETED"), Utility.combine(mesgUids.toArray(), ',')));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return search(searcher, listener);
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private Message[] search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException {
|
2009-11-26 00:10:12 -05:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
|
|
|
ArrayList<Message> messages = new ArrayList<Message>();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-04-21 00:22:02 -04:00
|
|
|
ArrayList<Integer> uids = new ArrayList<Integer>();
|
2009-11-26 00:10:12 -05:00
|
|
|
List<ImapResponse> responses = searcher.search(); //
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : responses) {
|
|
|
|
if (response.mTag == null) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
|
|
|
|
for (int i = 1, count = response.size(); i < count; i++) {
|
2010-03-24 21:24:21 -04:00
|
|
|
uids.add(Integer.parseInt(response.getString(i)));
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-24 21:24:21 -04:00
|
|
|
|
2009-04-21 00:22:02 -04:00
|
|
|
// Sort the uids in numerically ascending order
|
|
|
|
Collections.sort(uids);
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.size(); i < count; i++) {
|
|
|
|
if (listener != null) {
|
2009-04-21 00:22:02 -04:00
|
|
|
listener.messageStarted("" + uids.get(i), i, count);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-04-21 00:22:02 -04:00
|
|
|
ImapMessage message = new ImapMessage("" + uids.get(i), this);
|
2008-11-01 17:32:06 -04:00
|
|
|
messages.add(message);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
listener.messageFinished(message, i, count);
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
2010-08-07 11:10:07 -04:00
|
|
|
return messages.toArray(EMPTY_MESSAGE_ARRAY);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2009-11-17 11:54:50 -05:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
return getMessages(null, listener);
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2008-11-01 17:32:06 -04:00
|
|
|
public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
|
|
|
ArrayList<Message> messages = new ArrayList<Message>();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
if (uids == null) {
|
2009-04-21 00:22:02 -04:00
|
|
|
List<ImapResponse> responses = executeSimpleCommand("UID SEARCH 1:* NOT DELETED");
|
2008-11-01 17:32:06 -04:00
|
|
|
ArrayList<String> tempUids = new ArrayList<String>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : responses) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
|
|
|
|
for (int i = 1, count = response.size(); i < count; i++) {
|
2008-11-01 17:32:06 -04:00
|
|
|
tempUids.add(response.getString(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-08-07 11:10:07 -04:00
|
|
|
uids = tempUids.toArray(EMPTY_STRING_ARRAY);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = uids.length; i < count; i++) {
|
|
|
|
if (listener != null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
listener.messageStarted(uids[i], i, count);
|
|
|
|
}
|
|
|
|
ImapMessage message = new ImapMessage(uids[i], this);
|
|
|
|
messages.add(message);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
listener.messageFinished(message, i, count);
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
2010-08-07 11:10:07 -04:00
|
|
|
return messages.toArray(EMPTY_MESSAGE_ARRAY);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2008-11-01 17:32:06 -04:00
|
|
|
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
if (messages == null || messages.length == 0) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
checkOpen();
|
2010-08-10 23:36:40 -04:00
|
|
|
List<String> uids = new ArrayList<String>(messages.length);
|
2008-11-01 17:32:06 -04:00
|
|
|
HashMap<String, Message> messageMap = new HashMap<String, Message>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2010-08-10 23:36:40 -04:00
|
|
|
|
|
|
|
String uid = messages[i].getUid();
|
|
|
|
uids.add(uid);
|
|
|
|
messageMap.put(uid, messages[i]);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Figure out what command we are going to run:
|
|
|
|
* Flags - UID FETCH (FLAGS)
|
|
|
|
* Envelope - UID FETCH ([FLAGS] INTERNALDATE UID RFC822.SIZE FLAGS BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc)])
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
LinkedHashSet<String> fetchFields = new LinkedHashSet<String>();
|
|
|
|
fetchFields.add("UID");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.FLAGS)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
fetchFields.add("FLAGS");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.ENVELOPE)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
fetchFields.add("INTERNALDATE");
|
|
|
|
fetchFields.add("RFC822.SIZE");
|
2009-11-24 19:40:29 -05:00
|
|
|
fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc reply-to "
|
2011-01-12 18:48:28 -05:00
|
|
|
+ K9.IDENTITY_HEADER + ")]");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.STRUCTURE)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
fetchFields.add("BODYSTRUCTURE");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
|
2010-07-11 07:59:14 -04:00
|
|
|
fetchFields.add(String.format("BODY.PEEK[]<0.%d>", mAccount.getMaximumAutoDownloadMessageSize()));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.BODY)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
fetchFields.add("BODY.PEEK[]");
|
|
|
|
}
|
|
|
|
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2011-02-21 15:07:56 -05:00
|
|
|
for (int windowStart = 0; windowStart < messages.length; windowStart += (FETCH_WINDOW_SIZE)) {
|
2011-02-06 17:09:48 -05:00
|
|
|
List<String> uidWindow = uids.subList(windowStart, Math.min((windowStart + FETCH_WINDOW_SIZE), messages.length));
|
2010-08-10 23:36:40 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-08-10 23:36:40 -04:00
|
|
|
mConnection.sendCommand(String.format("UID FETCH %s (%s)",
|
2010-08-17 22:48:55 -04:00
|
|
|
Utility.combine(uidWindow.toArray(new String[uidWindow.size()]), ','),
|
2010-08-10 23:36:40 -04:00
|
|
|
Utility.combine(fetchFields.toArray(new String[fetchFields.size()]), ' ')
|
|
|
|
), false);
|
|
|
|
ImapResponse response;
|
|
|
|
int messageNumber = 0;
|
|
|
|
|
|
|
|
ImapResponseParser.IImapResponseCallback callback = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fp.contains(FetchProfile.Item.BODY) || fp.contains(FetchProfile.Item.BODY_SANE)) {
|
2010-08-10 23:36:40 -04:00
|
|
|
callback = new FetchBodyCallback(messageMap);
|
|
|
|
}
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
do {
|
2010-08-10 23:36:40 -04:00
|
|
|
response = mConnection.readResponse(callback);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (response.mTag == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
2010-08-10 23:36:40 -04:00
|
|
|
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
|
|
|
|
String uid = fetchList.getKeyedString("UID");
|
|
|
|
int msgSeq = response.getNumber(0);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (uid != null) {
|
|
|
|
try {
|
2010-08-10 23:36:40 -04:00
|
|
|
msgSeqUidMap.put(msgSeq, uid);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-08-10 23:36:40 -04:00
|
|
|
Log.v(K9.LOG_TAG, "Stored uid '" + uid + "' for msgSeq " + msgSeq + " into map " /*+ msgSeqUidMap.toString() */);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-08-10 23:36:40 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to store uid '" + uid + "' for msgSeq " + msgSeq);
|
2010-05-15 15:35:07 -04:00
|
|
|
}
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
Message message = messageMap.get(uid);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (message == null) {
|
2010-08-10 23:36:40 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Do not have message in messageMap for UID " + uid + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
handleUntaggedResponse(response);
|
|
|
|
continue;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-08-10 23:36:40 -04:00
|
|
|
listener.messageStarted(uid, messageNumber++, messageMap.size());
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
ImapMessage imapMessage = (ImapMessage) message;
|
2010-02-10 08:52:25 -05:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
Object literal = handleFetchResponse(imapMessage, fetchList);
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (literal != null) {
|
|
|
|
if (literal instanceof String) {
|
2010-08-10 23:36:40 -04:00
|
|
|
String bodyString = (String)literal;
|
|
|
|
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
|
|
|
imapMessage.parse(bodyStream);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (literal instanceof Integer) {
|
2010-08-10 23:36:40 -04:00
|
|
|
// All the work was done in FetchBodyCallback.foundLiteral()
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-08-10 23:36:40 -04:00
|
|
|
// This shouldn't happen
|
|
|
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
|
|
|
}
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
2010-08-10 23:36:40 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-08-10 23:36:40 -04:00
|
|
|
listener.messageFinished(message, messageNumber, messageMap.size());
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-08-10 23:36:40 -04:00
|
|
|
handleUntaggedResponse(response);
|
2010-02-10 08:52:25 -05:00
|
|
|
}
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2010-08-10 23:36:40 -04:00
|
|
|
while (response.more());
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} while (response.mTag == null);
|
|
|
|
} catch (IOException ioe) {
|
2010-08-10 23:36:40 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
@Override
|
|
|
|
public void fetchPart(Message message, Part part, MessageRetrievalListener listener)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2010-05-19 09:31:48 -04:00
|
|
|
checkOpen();
|
|
|
|
|
|
|
|
String[] parts = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (parts == null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
return;
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
String fetch;
|
|
|
|
String partId = parts[0];
|
2011-02-06 17:09:48 -05:00
|
|
|
if ("TEXT".equalsIgnoreCase(partId)) {
|
2010-07-11 07:59:14 -04:00
|
|
|
fetch = String.format("BODY.PEEK[TEXT]<0.%d>", mAccount.getMaximumAutoDownloadMessageSize());
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-19 09:31:48 -04:00
|
|
|
fetch = String.format("BODY.PEEK[%s]", partId);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-19 09:31:48 -04:00
|
|
|
mConnection.sendCommand(
|
2010-05-30 00:17:00 -04:00
|
|
|
String.format("UID FETCH %s (UID %s)", message.getUid(), fetch),
|
|
|
|
false);
|
2010-05-19 09:31:48 -04:00
|
|
|
|
|
|
|
ImapResponse response;
|
|
|
|
int messageNumber = 0;
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapResponseParser.IImapResponseCallback callback = new FetchPartCallback(part);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
do {
|
2010-05-19 09:31:48 -04:00
|
|
|
response = mConnection.readResponse(callback);
|
|
|
|
|
|
|
|
if ((response.mTag == null) &&
|
2011-02-06 17:09:48 -05:00
|
|
|
(ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))) {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
|
|
|
|
String uid = fetchList.getKeyedString("UID");
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!message.getUid().equals(uid)) {
|
2010-05-19 09:31:48 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Did not ask for UID " + uid + " for " + getLogId());
|
|
|
|
|
|
|
|
handleUntaggedResponse(response);
|
|
|
|
continue;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
listener.messageStarted(uid, messageNumber++, 1);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-02-10 08:52:25 -05:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapMessage imapMessage = (ImapMessage) message;
|
2010-02-14 10:51:09 -05:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
Object literal = handleFetchResponse(imapMessage, fetchList);
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (literal != null) {
|
|
|
|
if (literal instanceof Body) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// Most of the work was done in FetchAttchmentCallback.foundLiteral()
|
|
|
|
part.setBody((Body)literal);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (literal instanceof String) {
|
2010-02-14 10:51:09 -05:00
|
|
|
String bodyString = (String)literal;
|
2010-05-19 09:31:48 -04:00
|
|
|
InputStream bodyStream = new ByteArrayInputStream(bodyString.getBytes());
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-02-14 10:51:09 -05:00
|
|
|
String contentTransferEncoding = part.getHeader(
|
2010-05-30 00:17:00 -04:00
|
|
|
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
2010-02-14 10:51:09 -05:00
|
|
|
part.setBody(MimeUtility.decodeBody(bodyStream, contentTransferEncoding));
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-19 09:31:48 -04:00
|
|
|
// This shouldn't happen
|
|
|
|
throw new MessagingException("Got FETCH response with bogus parameters");
|
2010-02-10 08:52:25 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
listener.messageFinished(message, messageNumber, 1);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-26 00:10:12 -05:00
|
|
|
handleUntaggedResponse(response);
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
while (response.more());
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} while (response.mTag == null);
|
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
// Returns value of body field
|
2011-02-06 17:09:48 -05:00
|
|
|
private Object handleFetchResponse(ImapMessage message, ImapList fetchList) throws MessagingException {
|
2010-05-19 09:31:48 -04:00
|
|
|
Object result = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fetchList.containsKey("FLAGS")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapList flags = fetchList.getKeyedList("FLAGS");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (flags != null) {
|
|
|
|
for (int i = 0, count = flags.size(); i < count; i++) {
|
2010-05-19 09:31:48 -04:00
|
|
|
String flag = flags.getString(i);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (flag.equalsIgnoreCase("\\Deleted")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
message.setFlagInternal(Flag.DELETED, true);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag.equalsIgnoreCase("\\Answered")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
message.setFlagInternal(Flag.ANSWERED, true);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag.equalsIgnoreCase("\\Seen")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
message.setFlagInternal(Flag.SEEN, true);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag.equalsIgnoreCase("\\Flagged")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
message.setFlagInternal(Flag.FLAGGED, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fetchList.containsKey("INTERNALDATE")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
Date internalDate = fetchList.getKeyedDate("INTERNALDATE");
|
|
|
|
message.setInternalDate(internalDate);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fetchList.containsKey("RFC822.SIZE")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
int size = fetchList.getKeyedNumber("RFC822.SIZE");
|
|
|
|
message.setSize(size);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fetchList.containsKey("BODYSTRUCTURE")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapList bs = fetchList.getKeyedList("BODYSTRUCTURE");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bs != null) {
|
|
|
|
try {
|
2010-05-19 09:31:48 -04:00
|
|
|
parseBodyStructure(bs, message, "TEXT");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException e) {
|
2010-05-19 09:31:48 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Error handling message for " + getLogId(), e);
|
|
|
|
message.setBody(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (fetchList.containsKey("BODY")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
int index = fetchList.getKeyIndex("BODY") + 2;
|
|
|
|
result = fetchList.getObject(index);
|
|
|
|
|
|
|
|
// Check if there's an origin octet
|
2011-02-06 17:09:48 -05:00
|
|
|
if (result instanceof String) {
|
2010-05-19 09:31:48 -04:00
|
|
|
String originOctet = (String)result;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (originOctet.startsWith("<")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
result = fetchList.getObject(index + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Flag[] getPermanentFlags() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return PERMANENT_FLAGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle any untagged responses that the caller doesn't care to handle themselves.
|
|
|
|
* @param responses
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
protected List<ImapResponse> handleUntaggedResponses(List<ImapResponse> responses) {
|
|
|
|
for (ImapResponse response : responses) {
|
2008-11-01 17:32:06 -04:00
|
|
|
handleUntaggedResponse(response);
|
|
|
|
}
|
2009-04-21 00:22:02 -04:00
|
|
|
return responses;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void handlePossibleUidNext(ImapResponse response) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "OK") && response.size() > 1) {
|
2009-11-28 09:51:44 -05:00
|
|
|
Object bracketedObj = response.get(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bracketedObj instanceof ImapList) {
|
2009-11-28 09:51:44 -05:00
|
|
|
ImapList bracketed = (ImapList)bracketedObj;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bracketed.size() > 1) {
|
2009-11-28 09:51:44 -05:00
|
|
|
Object keyObj = bracketed.get(0);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (keyObj instanceof String) {
|
2009-11-28 09:51:44 -05:00
|
|
|
String key = (String)keyObj;
|
2011-02-06 17:09:48 -05:00
|
|
|
if ("UIDNEXT".equalsIgnoreCase(key)) {
|
2009-11-28 09:51:44 -05:00
|
|
|
uidNext = bracketed.getNumber(1);
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle an untagged response that the caller doesn't care to handle themselves.
|
|
|
|
* @param response
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void handleUntaggedResponse(ImapResponse response) {
|
|
|
|
if (response.mTag == null && response.size() > 1) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(1), "EXISTS")) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mMessageCount = response.getNumber(0);
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
handlePossibleUidNext(response);
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(1), "EXPUNGE") && mMessageCount > 0) {
|
2009-11-24 19:40:29 -05:00
|
|
|
mMessageCount--;
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.d(K9.LOG_TAG, "Got untagged EXPUNGE with mMessageCount " + mMessageCount + " for " + getLogId());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-07 15:27:30 -05:00
|
|
|
// if (response.size() > 1) {
|
|
|
|
// Object bracketedObj = response.get(1);
|
|
|
|
// if (bracketedObj instanceof ImapList)
|
|
|
|
// {
|
|
|
|
// ImapList bracketed = (ImapList)bracketedObj;
|
2009-11-24 19:40:29 -05:00
|
|
|
//
|
2009-11-07 15:27:30 -05:00
|
|
|
// if (bracketed.size() > 0)
|
|
|
|
// {
|
|
|
|
// Object keyObj = bracketed.get(0);
|
|
|
|
// if (keyObj instanceof String)
|
|
|
|
// {
|
|
|
|
// String key = (String)keyObj;
|
2010-05-09 11:27:41 -04:00
|
|
|
// if ("ALERT".equalsIgnoreCase(key))
|
2009-11-07 15:27:30 -05:00
|
|
|
// {
|
|
|
|
// StringBuffer sb = new StringBuffer();
|
|
|
|
// for (int i = 2, count = response.size(); i < count; i++) {
|
|
|
|
// sb.append(response.get(i).toString());
|
|
|
|
// sb.append(' ');
|
|
|
|
// }
|
2009-11-24 19:40:29 -05:00
|
|
|
//
|
2009-12-14 21:50:53 -05:00
|
|
|
// Log.w(K9.LOG_TAG, "ALERT: " + sb.toString() + " for " + getLogId());
|
2009-11-07 15:27:30 -05:00
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
2009-11-24 19:40:29 -05:00
|
|
|
//
|
|
|
|
//
|
2009-11-07 15:27:30 -05:00
|
|
|
// }
|
|
|
|
// }
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2009-12-14 21:50:53 -05:00
|
|
|
//Log.i(K9.LOG_TAG, "mMessageCount = " + mMessageCount + " for " + getLogId());
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private void parseBodyStructure(ImapList bs, Part part, String id)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
|
|
|
if (bs.get(0) instanceof ImapList) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* This is a multipart/*
|
|
|
|
*/
|
|
|
|
MimeMultipart mp = new MimeMultipart();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = bs.size(); i < count; i++) {
|
|
|
|
if (bs.get(i) instanceof ImapList) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* For each part in the message we're going to add a new BodyPart and parse
|
|
|
|
* into it.
|
|
|
|
*/
|
|
|
|
ImapBodyPart bp = new ImapBodyPart();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (id.equalsIgnoreCase("TEXT")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
parseBodyStructure(bs.getList(i), bp, Integer.toString(i + 1));
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
parseBodyStructure(bs.getList(i), bp, id + "." + (i + 1));
|
|
|
|
}
|
|
|
|
mp.addBodyPart(bp);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* We've got to the end of the children of the part, so now we can find out
|
|
|
|
* what type it is and bail out.
|
|
|
|
*/
|
|
|
|
String subType = bs.getString(i);
|
|
|
|
mp.setSubType(subType.toLowerCase());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
part.setBody(mp);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* This is a body. We need to add as much information as we can find out about
|
|
|
|
* it to the Part.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2011-03-15 03:34:38 -04:00
|
|
|
* 0| 0 body type
|
|
|
|
* 1| 1 body subtype
|
|
|
|
* 2| 2 body parameter parenthesized list
|
|
|
|
* 3| 3 body id (unused)
|
|
|
|
* 4| 4 body description (unused)
|
|
|
|
* 5| 5 body encoding
|
|
|
|
* 6| 6 body size
|
|
|
|
* -| 7 text lines (only for type TEXT, unused)
|
|
|
|
* Extensions (optional):
|
|
|
|
* 7| 8 body MD5 (unused)
|
|
|
|
* 8| 9 body disposition
|
|
|
|
* 9|10 body language (unused)
|
|
|
|
* 10|11 body location (unused)
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
String type = bs.getString(0);
|
|
|
|
String subType = bs.getString(1);
|
|
|
|
String mimeType = (type + "/" + subType).toLowerCase();
|
|
|
|
|
|
|
|
ImapList bodyParams = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bs.get(2) instanceof ImapList) {
|
2008-11-01 17:32:06 -04:00
|
|
|
bodyParams = bs.getList(2);
|
|
|
|
}
|
|
|
|
String encoding = bs.getString(5);
|
|
|
|
int size = bs.getNumber(6);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (MimeUtility.mimeTypeMatches(mimeType, "message/rfc822")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
// A body type of type MESSAGE and subtype RFC822
|
|
|
|
// contains, immediately after the basic fields, the
|
|
|
|
// envelope structure, body structure, and size in
|
|
|
|
// text lines of the encapsulated message.
|
|
|
|
// [MESSAGE, RFC822, [NAME, Fwd: [#HTR-517941]: update plans at 1am Friday - Memory allocation - displayware.eml], NIL, NIL, 7BIT, 5974, NIL, [INLINE, [FILENAME*0, Fwd: [#HTR-517941]: update plans at 1am Friday - Memory all, FILENAME*1, ocation - displayware.eml]], NIL]
|
|
|
|
/*
|
|
|
|
* This will be caught by fetch and handled appropriately.
|
|
|
|
*/
|
|
|
|
throw new MessagingException("BODYSTRUCTURE message/rfc822 not yet supported.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the content type with as much information as we know right now.
|
|
|
|
*/
|
|
|
|
String contentType = String.format("%s", mimeType);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bodyParams != null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
/*
|
|
|
|
* If there are body params we might be able to get some more information out
|
|
|
|
* of them.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = bodyParams.size(); i < count; i += 2) {
|
2008-11-01 17:32:06 -04:00
|
|
|
contentType += String.format(";\n %s=\"%s\"",
|
2009-11-24 19:40:29 -05:00
|
|
|
bodyParams.getString(i),
|
|
|
|
bodyParams.getString(i + 1));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
|
|
|
|
|
|
|
|
// Extension items
|
|
|
|
ImapList bodyDisposition = null;
|
|
|
|
if (("text".equalsIgnoreCase(type))
|
2011-03-15 03:34:38 -04:00
|
|
|
&& (bs.size() > 9)
|
2011-02-06 17:09:48 -05:00
|
|
|
&& (bs.get(9) instanceof ImapList)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
bodyDisposition = bs.getList(9);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (!("text".equalsIgnoreCase(type))
|
2011-03-15 03:34:38 -04:00
|
|
|
&& (bs.size() > 8)
|
2011-02-06 17:09:48 -05:00
|
|
|
&& (bs.get(8) instanceof ImapList)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
bodyDisposition = bs.getList(8);
|
|
|
|
}
|
|
|
|
|
|
|
|
String contentDisposition = "";
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (bodyDisposition != null && bodyDisposition.size() > 0) {
|
|
|
|
if (!"NIL".equalsIgnoreCase(bodyDisposition.getString(0))) {
|
2008-11-01 17:32:06 -04:00
|
|
|
contentDisposition = bodyDisposition.getString(0).toLowerCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((bodyDisposition.size() > 1)
|
2011-02-06 17:09:48 -05:00
|
|
|
&& (bodyDisposition.get(1) instanceof ImapList)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
ImapList bodyDispositionParams = bodyDisposition.getList(1);
|
|
|
|
/*
|
|
|
|
* If there is body disposition information we can pull some more information
|
|
|
|
* about the attachment out.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = bodyDispositionParams.size(); i < count; i += 2) {
|
2008-11-01 17:32:06 -04:00
|
|
|
contentDisposition += String.format(";\n %s=\"%s\"",
|
2009-11-24 19:40:29 -05:00
|
|
|
bodyDispositionParams.getString(i).toLowerCase(),
|
|
|
|
bodyDispositionParams.getString(i + 1));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (MimeUtility.getHeaderParameter(contentDisposition, "size") == null) {
|
2008-11-01 17:32:06 -04:00
|
|
|
contentDisposition += String.format(";\n size=%d", size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the content disposition containing at least the size. Attachment
|
|
|
|
* handling code will use this down the road.
|
|
|
|
*/
|
|
|
|
part.setHeader(MimeHeader.HEADER_CONTENT_DISPOSITION, contentDisposition);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the Content-Transfer-Encoding header. Attachment code will use this
|
|
|
|
* to parse the body.
|
|
|
|
*/
|
|
|
|
part.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (part instanceof ImapMessage) {
|
2008-11-01 17:32:06 -04:00
|
|
|
((ImapMessage) part).setSize(size);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (part instanceof ImapBodyPart) {
|
2008-11-01 17:32:06 -04:00
|
|
|
((ImapBodyPart) part).setSize(size);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException("Unknown part type " + part.toString());
|
|
|
|
}
|
|
|
|
part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Appends the given messages to the selected folder. This implementation also determines
|
|
|
|
* the new UID of the given message on the IMAP server and sets the Message's UID to the
|
|
|
|
* new server UID.
|
|
|
|
*/
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void appendMessages(Message[] messages) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
for (Message message : messages) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mConnection.sendCommand(
|
2011-01-05 19:39:09 -05:00
|
|
|
String.format("APPEND %s (%s) {%d}",
|
|
|
|
encodeString(encodeFolderName(getPrefixedName())),
|
2009-11-24 19:40:29 -05:00
|
|
|
combineFlags(message.getFlags()),
|
2011-03-27 11:07:03 -04:00
|
|
|
message.calculateSize()), false);
|
2008-11-01 17:32:06 -04:00
|
|
|
ImapResponse response;
|
2011-02-06 17:09:48 -05:00
|
|
|
do {
|
2008-11-01 17:32:06 -04:00
|
|
|
response = mConnection.readResponse();
|
2009-04-21 00:22:02 -04:00
|
|
|
handleUntaggedResponse(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (response.mCommandContinuationRequested) {
|
2011-03-27 11:07:03 -04:00
|
|
|
EOLConvertingOutputStream eolOut = new EOLConvertingOutputStream(mConnection.mOut);
|
2008-11-01 17:32:06 -04:00
|
|
|
message.writeTo(eolOut);
|
|
|
|
eolOut.write('\r');
|
|
|
|
eolOut.write('\n');
|
|
|
|
eolOut.flush();
|
|
|
|
}
|
|
|
|
while (response.more());
|
2011-02-06 17:09:48 -05:00
|
|
|
} while (response.mTag == null);
|
2008-11-01 17:32:06 -04:00
|
|
|
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
String newUid = getUidFromMessageId(message);
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (newUid != null) {
|
2009-11-24 19:40:29 -05:00
|
|
|
message.setUid(newUid);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getUidFromMessageId(Message message) throws MessagingException {
|
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
/*
|
2009-11-26 00:10:12 -05:00
|
|
|
* Try to find the UID of the message we just appended using the
|
|
|
|
* Message-ID header.
|
|
|
|
*/
|
2009-11-24 19:40:29 -05:00
|
|
|
String[] messageIdHeader = message.getHeader("Message-ID");
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messageIdHeader == null || messageIdHeader.length == 0) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Did not get a message-id in order to search for UID for " + getLogId());
|
2009-11-24 19:40:29 -05:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
String messageId = messageIdHeader[0];
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Looking for UID for message with message-id " + messageId + " for " + getLogId());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
List<ImapResponse> responses =
|
|
|
|
executeSimpleCommand(
|
2010-02-03 12:25:15 -05:00
|
|
|
String.format("UID SEARCH HEADER MESSAGE-ID %s", messageId));
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response1 : responses) {
|
2010-05-09 11:27:41 -04:00
|
|
|
if (response1.mTag == null && ImapResponseParser.equalsIgnoreCase(response1.get(0), "SEARCH")
|
2011-02-06 17:09:48 -05:00
|
|
|
&& response1.size() > 1) {
|
2009-11-24 19:40:29 -05:00
|
|
|
return response1.getString(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw new MessagingException("Could not find UID for message based on Message-ID", ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void expunge() throws MessagingException {
|
2009-11-24 19:40:29 -05:00
|
|
|
checkOpen();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
executeSimpleCommand("EXPUNGE");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String combineFlags(Flag[] flags) {
|
2009-11-24 19:40:29 -05:00
|
|
|
ArrayList<String> flagNames = new ArrayList<String>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Flag flag : flags) {
|
|
|
|
if (flag == Flag.SEEN) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Seen");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.DELETED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Deleted");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.ANSWERED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Answered");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.FLAGGED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Flagged");
|
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
return Utility.combine(flagNames.toArray(new String[flagNames.size()]), ' ');
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setFlags(Flag[] flags, boolean value)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
executeSimpleCommand(String.format("UID STORE 1:* %sFLAGS.SILENT (%s)",
|
|
|
|
value ? "+" : "-", combineFlags(flags)));
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
2009-05-04 20:42:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getNewPushState(String oldPushStateS, Message message) {
|
|
|
|
try {
|
2009-10-21 20:41:06 -04:00
|
|
|
String messageUidS = message.getUid();
|
|
|
|
int messageUid = Integer.parseInt(messageUidS);
|
|
|
|
ImapPushState oldPushState = ImapPushState.parse(oldPushStateS);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messageUid >= oldPushState.uidNext) {
|
2009-10-21 20:41:06 -04:00
|
|
|
int uidNext = messageUid + 1;
|
|
|
|
ImapPushState newPushState = new ImapPushState(uidNext);
|
|
|
|
return newPushState.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-10-21 20:41:06 -04:00
|
|
|
return null;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Exception while updated push state for " + getLogId(), e);
|
2009-10-21 20:41:06 -04:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2008-11-01 17:32:06 -04:00
|
|
|
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
checkOpen();
|
|
|
|
String[] uids = new String[messages.length];
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0, count = messages.length; i < count; i++) {
|
2008-11-01 17:32:06 -04:00
|
|
|
uids[i] = messages[i].getUid();
|
|
|
|
}
|
|
|
|
ArrayList<String> flagNames = new ArrayList<String>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Flag flag : flags) {
|
|
|
|
if (flag == Flag.SEEN) {
|
2008-11-01 17:32:06 -04:00
|
|
|
flagNames.add("\\Seen");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.DELETED) {
|
2008-11-01 17:32:06 -04:00
|
|
|
flagNames.add("\\Deleted");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.ANSWERED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Answered");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (flag == Flag.FLAGGED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
flagNames.add("\\Flagged");
|
2009-01-13 00:52:37 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-04-21 00:22:02 -04:00
|
|
|
executeSimpleCommand(String.format("UID STORE %s %sFLAGS.SILENT (%s)",
|
2009-11-24 19:40:29 -05:00
|
|
|
Utility.combine(uids, ','),
|
|
|
|
value ? "+" : "-",
|
|
|
|
Utility.combine(flagNames.toArray(new String[flagNames.size()]), ' ')));
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw ioExceptionHandler(mConnection, ioe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void checkOpen() throws MessagingException {
|
|
|
|
if (!isOpen()) {
|
2008-11-03 02:01:46 -05:00
|
|
|
throw new MessagingException("Folder " + getPrefixedName() + " is not open.");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private MessagingException ioExceptionHandler(ImapConnection connection, IOException ioe) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "IOException for " + getLogId(), ioe);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (connection != null) {
|
2010-05-30 17:20:47 -04:00
|
|
|
connection.close();
|
|
|
|
}
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
close();
|
2008-11-01 17:32:06 -04:00
|
|
|
return new MessagingException("IO Error", ioe);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
if (o instanceof ImapFolder) {
|
2010-05-22 13:34:16 -04:00
|
|
|
return ((ImapFolder)o).getName().equalsIgnoreCase(getName());
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
return super.equals(o);
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public int hashCode() {
|
2010-05-22 13:34:16 -04:00
|
|
|
return getName().hashCode();
|
2010-04-16 08:20:10 -04:00
|
|
|
}
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected ImapStore getStore() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return store;
|
|
|
|
}
|
2009-11-22 12:01:04 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected String getLogId() {
|
2010-04-14 23:17:25 -04:00
|
|
|
String id = getAccount().getDescription() + ":" + getName() + "/" + Thread.currentThread().getName();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mConnection != null) {
|
2009-11-24 19:40:29 -05:00
|
|
|
id += "/" + mConnection.getLogId();
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A cacheable class that stores the details for a single IMAP connection.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public static class ImapConnection {
|
2010-12-30 16:57:59 -05:00
|
|
|
protected Socket mSocket;
|
|
|
|
protected PeekableInputStream mIn;
|
|
|
|
protected OutputStream mOut;
|
|
|
|
protected ImapResponseParser mParser;
|
|
|
|
protected int mNextCommandTag;
|
2009-10-21 20:41:06 -04:00
|
|
|
protected Set<String> capabilities = new HashSet<String>();
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
private ImapSettings mSettings;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapConnection(final ImapSettings settings) {
|
2010-12-30 16:57:59 -05:00
|
|
|
this.mSettings = settings;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected String getLogId() {
|
2009-11-22 12:01:04 -05:00
|
|
|
return "conn" + hashCode();
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private List<ImapResponse> receiveCapabilities(List<ImapResponse> responses) {
|
|
|
|
for (ImapResponse response : responses) {
|
2010-02-27 12:34:38 -05:00
|
|
|
ImapList capabilityList = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (response.size() > 0 && ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
|
|
|
|
for (Object thisPart : response) {
|
|
|
|
if (thisPart instanceof ImapList) {
|
2010-02-27 12:34:38 -05:00
|
|
|
ImapList thisList = (ImapList)thisPart;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(thisList.get(0), CAPABILITY_CAPABILITY)) {
|
2010-02-27 12:34:38 -05:00
|
|
|
capabilityList = thisList;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (response.mTag == null) {
|
2010-02-27 12:34:38 -05:00
|
|
|
capabilityList = response;
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (capabilityList != null) {
|
|
|
|
if (capabilityList.size() > 0 && ImapResponseParser.equalsIgnoreCase(capabilityList.get(0), CAPABILITY_CAPABILITY)) {
|
|
|
|
if (K9.DEBUG) {
|
2010-02-27 12:34:38 -05:00
|
|
|
Log.d(K9.LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Object capability : capabilityList) {
|
|
|
|
if (capability instanceof String) {
|
2010-02-27 12:34:38 -05:00
|
|
|
// if (K9.DEBUG)
|
|
|
|
// {
|
|
|
|
// Log.v(K9.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
|
|
|
|
// }
|
2010-05-20 00:19:15 -04:00
|
|
|
capabilities.add(((String)capability).toUpperCase());
|
2010-02-27 12:34:38 -05:00
|
|
|
}
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-02-27 12:34:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return responses;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void open() throws IOException, MessagingException {
|
|
|
|
if (isOpen()) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-02-24 20:50:20 -05:00
|
|
|
boolean authSuccess = false;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
mNextCommandTag = 1;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
Security.setProperty("networkaddress.cache.ttl", "0");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.w(K9.LOG_TAG, "Could not set DNS ttl to 0 for " + getLogId(), e);
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-11-20 21:10:59 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-20 21:10:59 -05:00
|
|
|
Security.setProperty("networkaddress.cache.negative.ttl", "0");
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-11-20 21:10:59 -05:00
|
|
|
Log.w(K9.LOG_TAG, "Could not set DNS negative ttl to 0 for " + getLogId(), e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
SocketAddress socketAddress = new InetSocketAddress(mSettings.getHost(), mSettings.getPort());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
2010-12-30 16:57:59 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Connection " + getLogId() + " connecting to " + mSettings.getHost() + " @ IP addr " + socketAddress);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
if (mSettings.getConnectionSecurity() == CONNECTION_SECURITY_SSL_REQUIRED ||
|
2011-02-06 17:09:48 -05:00
|
|
|
mSettings.getConnectionSecurity() == CONNECTION_SECURITY_SSL_OPTIONAL) {
|
2008-11-01 17:32:06 -04:00
|
|
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
2010-12-30 16:57:59 -05:00
|
|
|
final boolean secure = mSettings.getConnectionSecurity() == CONNECTION_SECURITY_SSL_REQUIRED;
|
2011-02-06 17:09:48 -05:00
|
|
|
sslContext.init(null, new TrustManager[] {
|
2010-12-30 16:57:59 -05:00
|
|
|
TrustManagerFactory.get(mSettings.getHost(), secure)
|
2009-11-26 00:10:12 -05:00
|
|
|
}, new SecureRandom());
|
2008-11-01 17:32:06 -04:00
|
|
|
mSocket = sslContext.getSocketFactory().createSocket();
|
|
|
|
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
mSocket = new Socket();
|
|
|
|
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
setReadTimeout(Store.SOCKET_READ_TIMEOUT);
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
2009-11-24 19:40:29 -05:00
|
|
|
1024));
|
2008-11-01 17:32:06 -04:00
|
|
|
mParser = new ImapResponseParser(mIn);
|
|
|
|
mOut = mSocket.getOutputStream();
|
|
|
|
|
2010-02-27 12:34:38 -05:00
|
|
|
capabilities.clear();
|
2009-11-22 12:01:04 -05:00
|
|
|
ImapResponse nullResponse = mParser.readResponse();
|
2010-07-13 17:16:42 -04:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + "<<<" + nullResponse);
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2010-02-27 12:34:38 -05:00
|
|
|
List<ImapResponse> nullResponses = new LinkedList<ImapResponse>();
|
|
|
|
nullResponses.add(nullResponse);
|
|
|
|
receiveCapabilities(nullResponses);
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!hasCapability(CAPABILITY_CAPABILITY)) {
|
2010-03-06 19:30:40 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Did not get capabilities in banner, requesting CAPABILITY for " + getLogId());
|
2010-02-27 12:34:38 -05:00
|
|
|
List<ImapResponse> responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
|
2011-02-06 17:09:48 -05:00
|
|
|
if (responses.size() != 2) {
|
2010-02-27 12:34:38 -05:00
|
|
|
throw new MessagingException("Invalid CAPABILITY response received");
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
if (mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_OPTIONAL
|
2011-02-06 17:09:48 -05:00
|
|
|
|| mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED) {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (hasCapability("STARTTLS")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
// STARTTLS
|
|
|
|
executeSimpleCommand("STARTTLS");
|
|
|
|
|
|
|
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
2010-12-30 16:57:59 -05:00
|
|
|
boolean secure = mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED;
|
2011-02-06 17:09:48 -05:00
|
|
|
sslContext.init(null, new TrustManager[] {
|
2010-12-30 16:57:59 -05:00
|
|
|
TrustManagerFactory.get(mSettings.getHost(), secure)
|
2009-11-26 00:10:12 -05:00
|
|
|
}, new SecureRandom());
|
2010-12-30 16:57:59 -05:00
|
|
|
mSocket = sslContext.getSocketFactory().createSocket(mSocket, mSettings.getHost(), mSettings.getPort(),
|
2009-11-24 19:40:29 -05:00
|
|
|
true);
|
2008-11-01 17:32:06 -04:00
|
|
|
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
|
|
|
mIn = new PeekableInputStream(new BufferedInputStream(mSocket
|
2009-11-24 19:40:29 -05:00
|
|
|
.getInputStream(), 1024));
|
2008-11-01 17:32:06 -04:00
|
|
|
mParser = new ImapResponseParser(mIn);
|
|
|
|
mOut = mSocket.getOutputStream();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mSettings.getConnectionSecurity() == CONNECTION_SECURITY_TLS_REQUIRED) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException("TLS not supported but required");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-08 18:47:34 -05:00
|
|
|
mOut = new BufferedOutputStream(mOut, 1024);
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-07-10 12:41:22 -04:00
|
|
|
// Yahoo! requires a custom IMAP command to work right over a non-3G network
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mSettings.getHost().endsWith("yahoo.com")) {
|
2010-05-09 11:27:41 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.v(K9.LOG_TAG, "Found Yahoo! account. Sending proprietary commands.");
|
|
|
|
executeSimpleCommand("ID (\"GUID\" \"1\")");
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mSettings.getAuthType() == AuthType.CRAM_MD5) {
|
2009-12-20 00:41:43 -05:00
|
|
|
authCramMD5();
|
2010-03-06 19:30:40 -05:00
|
|
|
// The authCramMD5 method called on the previous line does not allow for handling updated capabilities
|
2010-02-27 12:48:00 -05:00
|
|
|
// sent by the server. So, to make sure we update to the post-authentication capability list
|
|
|
|
// we fetch the capabilities here.
|
2010-03-06 19:30:40 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Updating capabilities after CRAM-MD5 authentication for " + getLogId());
|
2010-02-27 12:48:00 -05:00
|
|
|
List<ImapResponse> responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
|
2011-02-06 17:09:48 -05:00
|
|
|
if (responses.size() != 2) {
|
2010-02-27 12:48:00 -05:00
|
|
|
throw new MessagingException("Invalid CAPABILITY response received");
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (mSettings.getAuthType() == AuthType.PLAIN) {
|
2011-01-05 19:39:09 -05:00
|
|
|
receiveCapabilities(executeSimpleCommand(String.format("LOGIN %s %s", ImapStore.encodeString(mSettings.getUsername()), ImapStore.encodeString(mSettings.getPassword())), true));
|
2009-12-20 00:15:20 -05:00
|
|
|
}
|
2009-02-24 20:50:20 -05:00
|
|
|
authSuccess = true;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (ImapException ie) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new AuthenticationFailedException(ie.getAlertText(), ie);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new AuthenticationFailedException(null, me);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-03-06 19:30:40 -05:00
|
|
|
Log.d(K9.LOG_TAG, CAPABILITY_COMPRESS_DEFLATE + " = " + hasCapability(CAPABILITY_COMPRESS_DEFLATE));
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (hasCapability(CAPABILITY_COMPRESS_DEFLATE)) {
|
2010-03-06 19:30:40 -05:00
|
|
|
ConnectivityManager connectivityManager = (ConnectivityManager)K9.app.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
|
|
boolean useCompression = true;
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-03-06 19:30:40 -05:00
|
|
|
NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (netInfo != null) {
|
2010-03-06 19:30:40 -05:00
|
|
|
int type = netInfo.getType();
|
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "On network type " + type);
|
2010-12-30 16:57:59 -05:00
|
|
|
useCompression = mSettings.useCompression(type);
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-03-06 19:30:40 -05:00
|
|
|
}
|
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "useCompression " + useCompression);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (useCompression) {
|
|
|
|
try {
|
2010-03-06 19:30:40 -05:00
|
|
|
executeSimpleCommand(COMMAND_COMPRESS_DEFLATE);
|
2011-02-12 04:01:05 -05:00
|
|
|
Inflater inf = new Inflater(true);
|
|
|
|
InflaterInputStream zInputStream = new InflaterInputStream(mSocket.getInputStream(), inf);
|
2010-03-06 19:30:40 -05:00
|
|
|
mIn = new PeekableInputStream(new BufferedInputStream(zInputStream, 1024));
|
|
|
|
mParser = new ImapResponseParser(mIn);
|
|
|
|
ZOutputStream zOutputStream = new ZOutputStream(mSocket.getOutputStream(), JZlib.Z_BEST_SPEED, true);
|
|
|
|
mOut = new BufferedOutputStream(zOutputStream, 1024);
|
|
|
|
zOutputStream.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-03-06 19:30:40 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Compression enabled for " + getLogId());
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-03-06 19:30:40 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to negotiate compression", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "NAMESPACE = " + hasCapability(CAPABILITY_NAMESPACE)
|
2010-12-30 16:57:59 -05:00
|
|
|
+ ", mPathPrefix = " + mSettings.getPathPrefix());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mSettings.getPathPrefix() == null) {
|
|
|
|
if (hasCapability(CAPABILITY_NAMESPACE)) {
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "mPathPrefix is unset and server has NAMESPACE capability");
|
2009-11-28 09:51:44 -05:00
|
|
|
List<ImapResponse> namespaceResponses =
|
|
|
|
executeSimpleCommand(COMMAND_NAMESPACE);
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : namespaceResponses) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), COMMAND_NAMESPACE)) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got NAMESPACE response " + response + " on " + getLogId());
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2009-11-28 09:51:44 -05:00
|
|
|
Object personalNamespaces = response.get(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (personalNamespaces != null && personalNamespaces instanceof ImapList) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got personal namespaces: " + personalNamespaces);
|
2009-11-28 09:51:44 -05:00
|
|
|
ImapList bracketed = (ImapList)personalNamespaces;
|
|
|
|
Object firstNamespace = bracketed.get(0);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (firstNamespace != null && firstNamespace instanceof ImapList) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got first personal namespaces: " + firstNamespace);
|
2009-11-28 09:51:44 -05:00
|
|
|
bracketed = (ImapList)firstNamespace;
|
2010-12-30 16:57:59 -05:00
|
|
|
mSettings.setPathPrefix(bracketed.getString(0));
|
|
|
|
mSettings.setPathDelimeter(bracketed.getString(1));
|
|
|
|
mSettings.setCombinedPrefix(null);
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
2010-12-30 16:57:59 -05:00
|
|
|
Log.d(K9.LOG_TAG, "Got path '" + mSettings.getPathPrefix() + "' and separator '" + mSettings.getPathDelimeter() + "'");
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "mPathPrefix is unset but server does not have NAMESPACE capability");
|
2010-12-30 16:57:59 -05:00
|
|
|
mSettings.setPathPrefix("");
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mSettings.getPathDelimeter() == null) {
|
|
|
|
try {
|
2010-05-19 22:33:44 -04:00
|
|
|
List<ImapResponse> nameResponses =
|
|
|
|
executeSimpleCommand(String.format("LIST \"\" \"\""));
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : nameResponses) {
|
|
|
|
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "LIST")) {
|
2010-12-30 16:57:59 -05:00
|
|
|
mSettings.setPathDelimeter(response.getString(2));
|
|
|
|
mSettings.setCombinedPrefix(null);
|
2010-05-19 22:33:44 -04:00
|
|
|
if (K9.DEBUG)
|
2010-12-30 16:57:59 -05:00
|
|
|
Log.d(K9.LOG_TAG, "Got path delimeter '" + mSettings.getPathDelimeter() + "' for " + getLogId());
|
2010-05-19 22:33:44 -04:00
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-05-19 22:33:44 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to get path delimeter using LIST", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (SSLException e) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new CertificateValidationException(e.getMessage(), e);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (GeneralSecurityException gse) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new MessagingException(
|
2009-11-24 19:40:29 -05:00
|
|
|
"Unable to open connection to IMAP server due to security error.", gse);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (ConnectException ce) {
|
2009-11-24 19:40:29 -05:00
|
|
|
String ceMess = ce.getMessage();
|
|
|
|
String[] tokens = ceMess.split("-");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (tokens != null && tokens.length > 1 && tokens[1] != null) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Stripping host/port from ConnectionException for " + getLogId(), ce);
|
2009-11-24 19:40:29 -05:00
|
|
|
throw new ConnectException(tokens[1].trim());
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw ce;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
|
|
|
if (!authSuccess) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Failed to login, closing connection for " + getLogId());
|
2009-11-24 19:40:29 -05:00
|
|
|
close();
|
|
|
|
}
|
2009-02-24 20:50:20 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void authCramMD5() throws AuthenticationFailedException, MessagingException {
|
|
|
|
try {
|
2009-12-20 00:41:43 -05:00
|
|
|
String tag = sendCommand("AUTHENTICATE CRAM-MD5", false);
|
2011-04-07 11:11:32 -04:00
|
|
|
byte[] buf = new byte[1024];
|
2009-12-20 00:41:43 -05:00
|
|
|
int b64NonceLen = 0;
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < buf.length; i++) {
|
2011-04-07 11:11:32 -04:00
|
|
|
buf[i] = (byte)mIn.read();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (buf[i] == 0x0a) {
|
2009-12-20 00:41:43 -05:00
|
|
|
b64NonceLen = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (b64NonceLen == 0) {
|
2009-12-20 00:41:43 -05:00
|
|
|
throw new AuthenticationFailedException("Error negotiating CRAM-MD5: nonce too long.");
|
|
|
|
}
|
2011-04-07 11:11:32 -04:00
|
|
|
byte[] b64NonceTrim = new byte[b64NonceLen - 2];
|
2009-12-20 00:41:43 -05:00
|
|
|
System.arraycopy(buf, 1, b64NonceTrim, 0, b64NonceLen - 2);
|
2011-04-07 11:11:32 -04:00
|
|
|
|
|
|
|
byte[] b64CRAM = Authentication.computeCramMd5Bytes(mSettings.getUsername(),
|
|
|
|
mSettings.getPassword(), b64NonceTrim);
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-12-20 00:41:43 -05:00
|
|
|
mOut.write(b64CRAM);
|
|
|
|
mOut.write(new byte[] { 0x0d, 0x0a });
|
|
|
|
mOut.flush();
|
2011-04-07 11:11:32 -04:00
|
|
|
|
2009-12-20 00:41:43 -05:00
|
|
|
int respLen = 0;
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < buf.length; i++) {
|
2011-04-07 11:11:32 -04:00
|
|
|
buf[i] = (byte)mIn.read();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (buf[i] == 0x0a) {
|
2009-12-20 00:41:43 -05:00
|
|
|
respLen = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-04-07 11:11:32 -04:00
|
|
|
|
2009-12-20 00:41:43 -05:00
|
|
|
String toMatch = tag + " OK";
|
|
|
|
String respStr = new String(buf, 0, respLen);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!respStr.startsWith(toMatch)) {
|
2009-12-20 00:41:43 -05:00
|
|
|
throw new AuthenticationFailedException("CRAM-MD5 error: " + respStr);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2011-04-07 11:11:32 -04:00
|
|
|
throw new AuthenticationFailedException("CRAM-MD5 Auth Failed.", ioe);
|
2009-12-20 00:41:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void setReadTimeout(int millis) throws SocketException {
|
2010-07-03 11:22:54 -04:00
|
|
|
Socket sock = mSocket;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (sock != null) {
|
2010-07-03 11:22:54 -04:00
|
|
|
sock.setSoTimeout(millis);
|
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected boolean isIdleCapable() {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.v(K9.LOG_TAG, "Connection " + getLogId() + " has " + capabilities.size() + " capabilities");
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-28 09:51:44 -05:00
|
|
|
return capabilities.contains(CAPABILITY_IDLE);
|
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected boolean hasCapability(String capability) {
|
2010-05-20 07:55:42 -04:00
|
|
|
return capabilities.contains(capability.toUpperCase());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isOpen() {
|
2009-11-22 12:01:04 -05:00
|
|
|
return (mIn != null && mOut != null && mSocket != null && mSocket.isConnected() && !mSocket.isClosed());
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void close() {
|
2008-11-01 17:32:06 -04:00
|
|
|
// if (isOpen()) {
|
|
|
|
// try {
|
|
|
|
// executeSimpleCommand("LOGOUT");
|
|
|
|
// } catch (Exception e) {
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
// }
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
mIn.close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
mOut.close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2008-11-01 17:32:06 -04:00
|
|
|
mSocket.close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
mIn = null;
|
|
|
|
mOut = null;
|
|
|
|
mSocket = null;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapResponse readResponse() throws IOException, MessagingException {
|
2010-05-19 09:31:48 -04:00
|
|
|
return readResponse(null);
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapResponse readResponse(ImapResponseParser.IImapResponseCallback callback) throws IOException {
|
|
|
|
try {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapResponse response = mParser.readResponse(callback);
|
2010-07-13 17:16:42 -04:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
return response;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
close();
|
|
|
|
throw ioe;
|
2009-11-22 12:01:04 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void sendContinuation(String continuation) throws IOException {
|
2009-10-21 20:41:06 -04:00
|
|
|
mOut.write(continuation.getBytes());
|
|
|
|
mOut.write('\r');
|
|
|
|
mOut.write('\n');
|
|
|
|
mOut.flush();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-07-13 17:16:42 -04:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + ">>> " + continuation);
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
public String sendCommand(String command, boolean sensitive)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws MessagingException, IOException {
|
|
|
|
try {
|
2009-11-24 19:40:29 -05:00
|
|
|
open();
|
|
|
|
String tag = Integer.toString(mNextCommandTag++);
|
|
|
|
String commandToSend = tag + " " + command;
|
|
|
|
mOut.write(commandToSend.getBytes());
|
|
|
|
mOut.write('\r');
|
|
|
|
mOut.write('\n');
|
|
|
|
mOut.flush();
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP) {
|
|
|
|
if (sensitive && !K9.DEBUG_SENSITIVE) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + ">>> "
|
2009-11-24 19:40:29 -05:00
|
|
|
+ "[Command Hidden, Enable Sensitive Debug Logging To Show]");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + ">>> " + commandToSend);
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException ioe) {
|
2009-11-24 19:40:29 -05:00
|
|
|
close();
|
|
|
|
throw ioe;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (ImapException ie) {
|
2009-11-24 19:40:29 -05:00
|
|
|
close();
|
|
|
|
throw ie;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2009-11-24 19:40:29 -05:00
|
|
|
close();
|
|
|
|
throw me;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public List<ImapResponse> executeSimpleCommand(String command) throws IOException,
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapException, MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
return executeSimpleCommand(command, false);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive) throws IOException,
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapException, MessagingException {
|
2009-10-21 20:41:06 -04:00
|
|
|
return executeSimpleCommand(command, sensitive, null);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-12-30 16:57:59 -05:00
|
|
|
public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive, UntaggedHandler untaggedHandler)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException, ImapException, MessagingException {
|
2009-10-25 19:56:25 -04:00
|
|
|
String commandToLog = command;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (sensitive && !K9.DEBUG_SENSITIVE) {
|
2009-10-25 19:56:25 -04:00
|
|
|
commandToLog = "*sensitive*";
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
|
2010-07-11 08:31:47 -04:00
|
|
|
//if (K9.DEBUG)
|
|
|
|
// Log.v(K9.LOG_TAG, "Sending IMAP command " + commandToLog + " on connection " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
String tag = sendCommand(command, sensitive);
|
2010-07-11 08:31:47 -04:00
|
|
|
//if (K9.DEBUG)
|
|
|
|
// Log.v(K9.LOG_TAG, "Sent IMAP command " + commandToLog + " with tag " + tag + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
|
|
|
|
ImapResponse response;
|
2011-02-06 17:09:48 -05:00
|
|
|
do {
|
2009-11-24 19:40:29 -05:00
|
|
|
response = mParser.readResponse();
|
2010-07-13 17:16:42 -04:00
|
|
|
if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (response.mTag != null && !response.mTag.equalsIgnoreCase(tag)) {
|
2010-01-03 15:34:22 -05:00
|
|
|
Log.w(K9.LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
|
2009-11-24 19:40:29 -05:00
|
|
|
Iterator<ImapResponse> iter = responses.iterator();
|
2011-02-06 17:09:48 -05:00
|
|
|
while (iter.hasNext()) {
|
2009-11-24 19:40:29 -05:00
|
|
|
ImapResponse delResponse = iter.next();
|
|
|
|
if (delResponse.mTag != null || delResponse.size() < 2
|
2011-02-06 17:09:48 -05:00
|
|
|
|| (!ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXISTS") && !ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
|
2009-11-24 19:40:29 -05:00
|
|
|
iter.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
response.mTag = null;
|
|
|
|
continue;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (untaggedHandler != null) {
|
2009-11-24 19:40:29 -05:00
|
|
|
untaggedHandler.handleAsyncUntaggedResponse(response);
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
responses.add(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
} while (response.mTag == null);
|
|
|
|
if (response.size() < 1 || !ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
|
2009-11-24 19:40:29 -05:00
|
|
|
throw new ImapException("Command: " + commandToLog + "; response: " + response.toString(), response.getAlertText());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
return responses;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
static class ImapMessage extends MimeMessage {
|
|
|
|
ImapMessage(String uid, Folder folder) {
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mUid = uid;
|
|
|
|
this.mFolder = folder;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setSize(int size) {
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mSize = size;
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void parse(InputStream in) throws IOException, MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
super.parse(in);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
super.setFlag(flag, set);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
super.setFlag(flag, set);
|
|
|
|
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete(String trashFolderName) throws MessagingException {
|
2009-11-29 13:07:34 -05:00
|
|
|
getFolder().delete(new Message[] { this }, trashFolderName);
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
static class ImapBodyPart extends MimeBodyPart {
|
|
|
|
public ImapBodyPart() throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setSize(int size) {
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mSize = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
static class ImapException extends MessagingException {
|
2011-01-31 18:45:14 -05:00
|
|
|
private static final long serialVersionUID = 3725007182205882394L;
|
2008-11-01 17:32:06 -04:00
|
|
|
String mAlertText;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapException(String message, String alertText, Throwable throwable) {
|
2008-11-01 17:32:06 -04:00
|
|
|
super(message, throwable);
|
|
|
|
this.mAlertText = alertText;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapException(String message, String alertText) {
|
2008-11-01 17:32:06 -04:00
|
|
|
super(message);
|
|
|
|
this.mAlertText = alertText;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getAlertText() {
|
2008-11-01 17:32:06 -04:00
|
|
|
return mAlertText;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setAlertText(String alertText) {
|
2008-11-01 17:32:06 -04:00
|
|
|
mAlertText = alertText;
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public class ImapFolderPusher extends ImapFolder implements UntaggedHandler {
|
2009-11-22 12:01:04 -05:00
|
|
|
final PushReceiver receiver;
|
2009-10-21 20:41:06 -04:00
|
|
|
Thread listeningThread = null;
|
2009-11-22 12:01:04 -05:00
|
|
|
final AtomicBoolean stop = new AtomicBoolean(false);
|
|
|
|
final AtomicBoolean idling = new AtomicBoolean(false);
|
|
|
|
final AtomicBoolean doneSent = new AtomicBoolean(false);
|
|
|
|
final AtomicInteger delayTime = new AtomicInteger(NORMAL_DELAY_TIME);
|
2010-04-14 23:17:25 -04:00
|
|
|
final AtomicInteger idleFailureCount = new AtomicInteger(0);
|
2010-05-15 15:35:07 -04:00
|
|
|
final AtomicBoolean needsPoll = new AtomicBoolean(false);
|
2009-11-26 00:10:12 -05:00
|
|
|
List<ImapResponse> storedUntaggedResponses = new ArrayList<ImapResponse>();
|
2010-05-16 20:30:32 -04:00
|
|
|
TracingWakeLock wakeLock = null;
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapFolderPusher(ImapStore store, String name, PushReceiver nReceiver) {
|
2009-10-21 20:41:06 -04:00
|
|
|
super(store, name);
|
|
|
|
receiver = nReceiver;
|
2010-05-16 20:30:32 -04:00
|
|
|
TracingPowerManager pm = TracingPowerManager.getPowerManager(receiver.getContext());
|
|
|
|
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImapFolderPusher " + store.getAccount().getDescription() + ":" + getName());
|
|
|
|
wakeLock.setReferenceCounted(false);
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
public void refresh() throws IOException, MessagingException {
|
|
|
|
if (idling.get()) {
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
|
2009-10-21 20:41:06 -04:00
|
|
|
sendDone();
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void sendDone() throws IOException, MessagingException {
|
|
|
|
if (doneSent.compareAndSet(false, true)) {
|
2010-07-03 11:22:54 -04:00
|
|
|
ImapConnection conn = mConnection;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (conn != null) {
|
2010-07-03 11:22:54 -04:00
|
|
|
conn.setReadTimeout(Store.SOCKET_READ_TIMEOUT);
|
|
|
|
sendContinuation("DONE");
|
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
private void sendContinuation(String continuation)
|
2011-02-06 17:09:48 -05:00
|
|
|
throws IOException {
|
2010-07-03 11:22:54 -04:00
|
|
|
ImapConnection conn = mConnection;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (conn != null) {
|
2010-07-03 11:22:54 -04:00
|
|
|
conn.sendContinuation(continuation);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void start() {
|
|
|
|
Runnable runner = new Runnable() {
|
|
|
|
public void run() {
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Pusher starting for " + getLogId());
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
while (!stop.get()) {
|
|
|
|
try {
|
2009-10-21 20:41:06 -04:00
|
|
|
int oldUidNext = -1;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-10-21 20:41:06 -04:00
|
|
|
String pushStateS = receiver.getPushState(getName());
|
|
|
|
ImapPushState pushState = ImapPushState.parse(pushStateS);
|
|
|
|
oldUidNext = pushState.uidNext;
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2010-04-26 23:02:17 -04:00
|
|
|
ImapConnection oldConnection = mConnection;
|
2010-05-15 15:35:07 -04:00
|
|
|
internalOpen(OpenMode.READ_ONLY);
|
2010-07-03 11:22:54 -04:00
|
|
|
ImapConnection conn = mConnection;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (conn == null) {
|
2009-10-21 20:41:06 -04:00
|
|
|
receiver.pushError("Could not establish connection for IDLE", null);
|
|
|
|
throw new MessagingException("Could not establish connection for IDLE");
|
|
|
|
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!conn.isIdleCapable()) {
|
2009-10-21 20:41:06 -04:00
|
|
|
stop.set(true);
|
2010-07-03 11:22:54 -04:00
|
|
|
receiver.pushError("IMAP server is not IDLE capable: " + conn.toString(), null);
|
|
|
|
throw new MessagingException("IMAP server is not IDLE capable:" + conn.toString());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2010-07-06 06:29:26 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!stop.get() && mAccount.isPushPollOnConnect() && (conn != oldConnection || needsPoll.getAndSet(false))) {
|
2010-05-15 15:35:07 -04:00
|
|
|
List<ImapResponse> untaggedResponses = new ArrayList<ImapResponse>(storedUntaggedResponses);
|
|
|
|
storedUntaggedResponses.clear();
|
|
|
|
processUntaggedResponses(untaggedResponses);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mMessageCount == -1) {
|
2010-07-03 11:22:54 -04:00
|
|
|
throw new MessagingException("Message count = -1 for idling");
|
|
|
|
}
|
2010-04-26 23:02:17 -04:00
|
|
|
receiver.syncFolder(ImapFolderPusher.this);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (stop.get()) {
|
2010-05-25 23:24:33 -04:00
|
|
|
continue;
|
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
int startUid = oldUidNext;
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-04-27 00:06:26 -04:00
|
|
|
int newUidNext = uidNext;
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (newUidNext == -1) {
|
|
|
|
if (K9.DEBUG) {
|
2010-04-27 00:06:26 -04:00
|
|
|
Log.d(K9.LOG_TAG, "uidNext is -1, using search to find highest UID");
|
|
|
|
}
|
|
|
|
int highestUid = getHighestUid();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (highestUid != -1) {
|
2010-04-27 00:06:26 -04:00
|
|
|
if (K9.DEBUG)
|
2010-05-09 11:27:41 -04:00
|
|
|
Log.d(K9.LOG_TAG, "highest UID = " + highestUid);
|
2010-04-27 00:06:26 -04:00
|
|
|
newUidNext = highestUid + 1;
|
|
|
|
if (K9.DEBUG)
|
2010-05-11 22:51:59 -04:00
|
|
|
Log.d(K9.LOG_TAG, "highest UID = " + highestUid
|
|
|
|
+ ", set newUidNext to " + newUidNext);
|
2010-04-27 00:06:26 -04:00
|
|
|
}
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startUid < newUidNext - mAccount.getDisplayCount()) {
|
2010-04-27 00:06:26 -04:00
|
|
|
startUid = newUidNext - mAccount.getDisplayCount();
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startUid < 1) {
|
2009-11-28 09:51:44 -05:00
|
|
|
startUid = 1;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (newUidNext > startUid) {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
2010-04-27 00:06:26 -04:00
|
|
|
Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid + " to " + newUidNext + " for " + getLogId());
|
2009-10-21 20:41:06 -04:00
|
|
|
List<Message> messages = new ArrayList<Message>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int uid = startUid; uid < newUidNext; uid++) {
|
2009-10-21 20:41:06 -04:00
|
|
|
ImapMessage message = new ImapMessage("" + uid, ImapFolderPusher.this);
|
|
|
|
messages.add(message);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messages.size() > 0) {
|
2009-10-21 20:41:06 -04:00
|
|
|
pushMessages(messages, true);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-25 23:24:33 -04:00
|
|
|
List<ImapResponse> untaggedResponses = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
while (storedUntaggedResponses.size() > 0) {
|
2010-05-25 23:24:33 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Processing " + storedUntaggedResponses.size() + " untagged responses from previous commands for " + getLogId());
|
|
|
|
untaggedResponses = new ArrayList<ImapResponse>(storedUntaggedResponses);
|
|
|
|
storedUntaggedResponses.clear();
|
|
|
|
processUntaggedResponses(untaggedResponses);
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-25 23:24:33 -04:00
|
|
|
if (K9.DEBUG)
|
2010-05-30 00:17:00 -04:00
|
|
|
Log.i(K9.LOG_TAG, "About to IDLE for " + getLogId());
|
2009-11-26 00:10:12 -05:00
|
|
|
|
2010-05-25 23:24:33 -04:00
|
|
|
receiver.setPushActive(getName(), true);
|
|
|
|
idling.set(true);
|
|
|
|
doneSent.set(false);
|
2010-07-06 06:29:26 -04:00
|
|
|
|
|
|
|
conn.setReadTimeout((getAccount().getIdleRefreshMinutes() * 60 * 1000) + IDLE_READ_TIMEOUT_INCREMENT);
|
2010-05-25 23:24:33 -04:00
|
|
|
untaggedResponses = executeSimpleCommand(COMMAND_IDLE, false, ImapFolderPusher.this);
|
|
|
|
idling.set(false);
|
|
|
|
delayTime.set(NORMAL_DELAY_TIME);
|
|
|
|
idleFailureCount.set(0);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
|
2009-11-26 00:10:12 -05:00
|
|
|
storedUntaggedResponses.clear();
|
2009-10-21 20:41:06 -04:00
|
|
|
idling.set(false);
|
|
|
|
receiver.setPushActive(getName(), false);
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception me) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Got exception while closing for exception for " + getLogId(), me);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (stop.get()) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Got exception while idling, but stop is set for " + getLogId());
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-04-14 23:17:25 -04:00
|
|
|
receiver.pushError("Push error for " + getName(), e);
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Got exception while idling for " + getLogId(), e);
|
2009-11-22 12:01:04 -05:00
|
|
|
int delayTimeInt = delayTime.get();
|
2010-05-16 20:30:32 -04:00
|
|
|
receiver.sleep(wakeLock, delayTimeInt);
|
2009-11-22 12:01:04 -05:00
|
|
|
delayTimeInt *= 2;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (delayTimeInt > MAX_DELAY_TIME) {
|
2009-11-22 12:01:04 -05:00
|
|
|
delayTimeInt = MAX_DELAY_TIME;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-22 12:01:04 -05:00
|
|
|
delayTime.set(delayTimeInt);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (idleFailureCount.incrementAndGet() > IDLE_FAILURE_COUNT_LIMIT) {
|
2010-04-14 23:17:25 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Disabling pusher for " + getLogId() + " after " + idleFailureCount.get() + " consecutive errors");
|
|
|
|
receiver.pushError("Push disabled for " + getName() + " after " + idleFailureCount.get() + " consecutive errors", e);
|
|
|
|
stop.set(true);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-26 00:10:12 -05:00
|
|
|
receiver.setPushActive(getName(), false);
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Pusher for " + getLogId() + " is exiting");
|
Implementation of complete IMAP two-phase "delete/expunge" behavior.
On each IMAP account, the expunge behavior can be set to expunge
messages in a folder as soon as a move or delete is performed on the
folder ("immediately"), each time the folder is polled, or only when
executed manually.
In the Message List, there is now an Expunge action in the option
menu.
In the Folder List, there is now an Expunge action in the context
menu (long-press on the folder).
For IMAP accounts, it is also possible to disable the copying of deleted messages to the
Trash folder, by setting the Trash folder to -NONE-.
Fixes Issue 536.
Separately, in WebDAV accounts, the user can now choose the
server-side equivalents of the special folders, just like for IMAP.
2009-12-20 18:13:49 -05:00
|
|
|
close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception me) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Got exception while closing for " + getLogId(), me);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.release();
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
listeningThread = new Thread(runner);
|
|
|
|
listeningThread.start();
|
|
|
|
}
|
|
|
|
|
2009-11-26 00:10:12 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void handleUntaggedResponse(ImapResponse response) {
|
|
|
|
if (response.mTag == null && response.size() > 1) {
|
2009-11-26 00:10:12 -05:00
|
|
|
Object responseType = response.get(1);
|
2010-05-09 11:27:41 -04:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")
|
|
|
|
|| ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE")
|
2011-02-06 17:09:48 -05:00
|
|
|
|| ImapResponseParser.equalsIgnoreCase(responseType, "EXISTS")) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Storing response " + response + " for later processing");
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-26 00:10:12 -05:00
|
|
|
storedUntaggedResponses.add(response);
|
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
handlePossibleUidNext(response);
|
2009-11-26 00:10:12 -05:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected void processUntaggedResponses(List<ImapResponse> responses) throws MessagingException {
|
2009-11-28 09:51:44 -05:00
|
|
|
boolean skipSync = false;
|
2009-10-21 20:41:06 -04:00
|
|
|
int oldMessageCount = mMessageCount;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (oldMessageCount == -1) {
|
2009-11-28 09:51:44 -05:00
|
|
|
skipSync = true;
|
|
|
|
}
|
2009-11-26 00:10:12 -05:00
|
|
|
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
|
2010-05-15 15:35:07 -04:00
|
|
|
List<String> removeMsgUids = new LinkedList<String>();
|
2009-10-21 20:41:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (ImapResponse response : responses) {
|
2010-05-15 15:35:07 -04:00
|
|
|
oldMessageCount += processUntaggedResponse(oldMessageCount, response, flagSyncMsgSeqs, removeMsgUids);
|
2009-11-26 00:10:12 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!skipSync) {
|
|
|
|
if (oldMessageCount < 0) {
|
2009-11-28 09:51:44 -05:00
|
|
|
oldMessageCount = 0;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mMessageCount > oldMessageCount) {
|
2010-04-05 22:39:57 -04:00
|
|
|
syncMessages(mMessageCount, true);
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
2010-05-19 22:33:44 -04:00
|
|
|
Log.d(K9.LOG_TAG, "UIDs for messages needing flag sync are " + flagSyncMsgSeqs + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (flagSyncMsgSeqs.size() > 0) {
|
2009-11-26 00:10:12 -05:00
|
|
|
syncMessages(flagSyncMsgSeqs);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (removeMsgUids.size() > 0) {
|
2010-05-15 15:35:07 -04:00
|
|
|
removeMessages(removeMsgUids);
|
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void syncMessages(int end, boolean newArrivals) throws MessagingException {
|
2010-04-05 22:39:57 -04:00
|
|
|
int oldUidNext = -1;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-04-05 22:39:57 -04:00
|
|
|
String pushStateS = receiver.getPushState(getName());
|
|
|
|
ImapPushState pushState = ImapPushState.parse(pushStateS);
|
|
|
|
oldUidNext = pushState.uidNext;
|
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-04-05 22:39:57 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-05-30 17:20:47 -04:00
|
|
|
Message[] messageArray = getMessages(end, end, null, true, null);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messageArray != null && messageArray.length > 0) {
|
2010-04-05 22:39:57 -04:00
|
|
|
int newUid = Integer.parseInt(messageArray[0].getUid());
|
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Got newUid " + newUid + " for message " + end + " on " + getLogId());
|
|
|
|
int startUid = oldUidNext;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startUid < newUid - 10) {
|
2010-04-05 22:39:57 -04:00
|
|
|
startUid = newUid - 10;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (startUid < 1) {
|
2010-04-05 22:39:57 -04:00
|
|
|
startUid = 1;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (newUid >= startUid) {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-04-05 22:39:57 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid + " to " + newUid + " for " + getLogId());
|
|
|
|
List<Message> messages = new ArrayList<Message>();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int uid = startUid; uid <= newUid; uid++) {
|
2010-04-05 22:39:57 -04:00
|
|
|
ImapMessage message = new ImapMessage("" + uid, ImapFolderPusher.this);
|
|
|
|
messages.add(message);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (messages.size() > 0) {
|
2010-04-05 22:39:57 -04:00
|
|
|
pushMessages(messages, true);
|
|
|
|
}
|
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void syncMessages(List<Integer> flagSyncMsgSeqs) {
|
|
|
|
try {
|
2009-11-26 00:10:12 -05:00
|
|
|
Message[] messageArray = null;
|
|
|
|
|
|
|
|
messageArray = getMessages(flagSyncMsgSeqs, true, null);
|
|
|
|
|
|
|
|
List<Message> messages = new ArrayList<Message>();
|
2010-11-30 22:04:57 -05:00
|
|
|
messages.addAll(Arrays.asList(messageArray));
|
2009-11-26 00:10:12 -05:00
|
|
|
pushMessages(messages, false);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-11-26 00:10:12 -05:00
|
|
|
receiver.pushError("Exception while processing Push untagged responses", e);
|
|
|
|
}
|
|
|
|
}
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void removeMessages(List<String> removeUids) {
|
2010-05-15 15:35:07 -04:00
|
|
|
List<Message> messages = new ArrayList<Message>(removeUids.size());
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-15 15:35:07 -04:00
|
|
|
Message[] existingMessages = getMessagesFromUids(removeUids, true, null);
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Message existingMessage : existingMessages) {
|
2010-05-15 15:35:07 -04:00
|
|
|
needsPoll.set(true);
|
|
|
|
msgSeqUidMap.clear();
|
|
|
|
String existingUid = existingMessage.getUid();
|
|
|
|
Log.w(K9.LOG_TAG, "Message with UID " + existingUid + " still exists on server, not expunging");
|
|
|
|
removeUids.remove(existingUid);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String uid : removeUids) {
|
2010-05-15 15:35:07 -04:00
|
|
|
ImapMessage message = new ImapMessage(uid, this);
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-15 15:35:07 -04:00
|
|
|
message.setFlagInternal(Flag.DELETED, true);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException me) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to set DELETED flag on message " + message.getUid());
|
|
|
|
}
|
|
|
|
messages.add(message);
|
|
|
|
}
|
|
|
|
receiver.messagesRemoved(this, messages);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Cannot remove EXPUNGEd messages", e);
|
|
|
|
}
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2010-05-15 15:35:07 -04:00
|
|
|
}
|
2009-11-26 00:10:12 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected int processUntaggedResponse(int oldMessageCount, ImapResponse response, List<Integer> flagSyncMsgSeqs, List<String> removeMsgUids) {
|
2009-10-21 20:41:06 -04:00
|
|
|
super.handleUntaggedResponse(response);
|
2009-11-26 00:10:12 -05:00
|
|
|
int messageCountDelta = 0;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (response.mTag == null && response.size() > 1) {
|
|
|
|
try {
|
2009-10-21 20:41:06 -04:00
|
|
|
Object responseType = response.get(1);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.i(K9.LOG_TAG, "Got FETCH " + response);
|
2009-10-21 20:41:06 -04:00
|
|
|
int msgSeq = response.getNumber(0);
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got untagged FETCH for msgseq " + msgSeq + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!flagSyncMsgSeqs.contains(msgSeq)) {
|
2009-11-26 00:10:12 -05:00
|
|
|
flagSyncMsgSeqs.add(msgSeq);
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE")) {
|
2009-11-26 00:10:12 -05:00
|
|
|
int msgSeq = response.getNumber(0);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (msgSeq <= oldMessageCount) {
|
2009-11-26 00:10:12 -05:00
|
|
|
messageCountDelta = -1;
|
|
|
|
}
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got untagged EXPUNGE for msgseq " + msgSeq + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-11-26 00:10:12 -05:00
|
|
|
List<Integer> newSeqs = new ArrayList<Integer>();
|
|
|
|
Iterator<Integer> flagIter = flagSyncMsgSeqs.iterator();
|
2011-02-06 17:09:48 -05:00
|
|
|
while (flagIter.hasNext()) {
|
2009-11-26 00:10:12 -05:00
|
|
|
Integer flagMsg = flagIter.next();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (flagMsg >= msgSeq) {
|
2009-11-26 00:10:12 -05:00
|
|
|
flagIter.remove();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (flagMsg > msgSeq) {
|
2009-11-26 00:10:12 -05:00
|
|
|
newSeqs.add(flagMsg--);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
flagSyncMsgSeqs.addAll(newSeqs);
|
2010-05-15 15:46:16 -04:00
|
|
|
|
|
|
|
|
2010-05-15 15:35:07 -04:00
|
|
|
List<Integer> msgSeqs = new ArrayList<Integer>(msgSeqUidMap.keySet());
|
|
|
|
Collections.sort(msgSeqs); // Have to do comparisons in order because of msgSeq reductions
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
for (Integer msgSeqNumI : msgSeqs) {
|
|
|
|
if (K9.DEBUG) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.v(K9.LOG_TAG, "Comparing EXPUNGEd msgSeq " + msgSeq + " to " + msgSeqNumI);
|
|
|
|
}
|
|
|
|
int msgSeqNum = msgSeqNumI;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (msgSeqNum == msgSeq) {
|
2010-05-15 15:35:07 -04:00
|
|
|
String uid = msgSeqUidMap.get(msgSeqNum);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.d(K9.LOG_TAG, "Scheduling removal of UID " + uid + " because msgSeq " + msgSeqNum + " was expunged");
|
|
|
|
}
|
|
|
|
removeMsgUids.add(uid);
|
|
|
|
msgSeqUidMap.remove(msgSeqNum);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (msgSeqNum > msgSeq) {
|
2010-05-15 15:35:07 -04:00
|
|
|
String uid = msgSeqUidMap.get(msgSeqNum);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-05-15 15:35:07 -04:00
|
|
|
Log.d(K9.LOG_TAG, "Reducing msgSeq for UID " + uid + " from " + msgSeqNum + " to " + (msgSeqNum - 1));
|
|
|
|
}
|
|
|
|
msgSeqUidMap.remove(msgSeqNum);
|
2011-02-06 17:09:48 -05:00
|
|
|
msgSeqUidMap.put(msgSeqNum - 1, uid);
|
2010-05-15 15:35:07 -04:00
|
|
|
}
|
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Could not handle untagged FETCH for " + getLogId(), e);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
2009-11-26 00:10:12 -05:00
|
|
|
return messageCountDelta;
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void pushMessages(List<Message> messages, boolean newArrivals) {
|
2009-10-21 20:41:06 -04:00
|
|
|
RuntimeException holdException = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
if (newArrivals) {
|
2009-11-26 00:10:12 -05:00
|
|
|
receiver.messagesArrived(this, messages);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-11-26 00:10:12 -05:00
|
|
|
receiver.messagesFlagsChanged(this, messages);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (RuntimeException e) {
|
2009-11-24 19:40:29 -05:00
|
|
|
holdException = e;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (holdException != null) {
|
2009-10-21 20:41:06 -04:00
|
|
|
throw holdException;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void stop() {
|
2009-10-21 20:41:06 -04:00
|
|
|
stop.set(true);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listeningThread != null) {
|
2009-11-28 09:51:44 -05:00
|
|
|
listeningThread.interrupt();
|
|
|
|
}
|
2010-07-03 11:22:54 -04:00
|
|
|
ImapConnection conn = mConnection;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (conn != null) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.v(K9.LOG_TAG, "Closing mConnection to stop pushing for " + getLogId());
|
2010-07-03 11:22:54 -04:00
|
|
|
conn.close();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.w(K9.LOG_TAG, "Attempt to interrupt null mConnection to stop pushing on folderPusher for " + getLogId());
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void handleAsyncUntaggedResponse(ImapResponse response) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.v(K9.LOG_TAG, "Got async response: " + response);
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (stop.get()) {
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got async untagged response: " + response + ", but stop is set for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-28 09:51:44 -05:00
|
|
|
sendDone();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
|
2009-12-06 19:56:06 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
|
|
|
if (response.mTag == null) {
|
|
|
|
if (response.size() > 1) {
|
2009-11-28 09:51:44 -05:00
|
|
|
boolean started = false;
|
|
|
|
Object responseType = response.get(1);
|
2010-05-09 11:27:41 -04:00
|
|
|
if (ImapResponseParser.equalsIgnoreCase(responseType, "EXISTS") || ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE") ||
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
|
|
|
|
if (!started) {
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
|
2009-11-28 09:51:44 -05:00
|
|
|
started = true;
|
|
|
|
}
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2009-12-14 21:50:53 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Got useful async untagged response: " + response + " for " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-11-28 09:51:44 -05:00
|
|
|
sendDone();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (response.mCommandContinuationRequested) {
|
2010-04-14 23:17:25 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Idling " + getLogId());
|
2010-01-02 21:00:20 -05:00
|
|
|
|
2010-05-16 20:30:32 -04:00
|
|
|
wakeLock.release();
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Pusher getPusher(PushReceiver receiver) {
|
2009-11-28 09:51:44 -05:00
|
|
|
return new ImapPusher(this, receiver);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public class ImapPusher implements Pusher {
|
2009-11-28 09:51:44 -05:00
|
|
|
final ImapStore mStore;
|
|
|
|
final PushReceiver mReceiver;
|
2010-05-09 11:27:41 -04:00
|
|
|
private long lastRefresh = -1;
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2009-11-28 09:51:44 -05:00
|
|
|
HashMap<String, ImapFolderPusher> folderPushers = new HashMap<String, ImapFolderPusher>();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapPusher(ImapStore store, PushReceiver receiver) {
|
2009-11-28 09:51:44 -05:00
|
|
|
mStore = store;
|
|
|
|
mReceiver = receiver;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void start(List<String> folderNames) {
|
2009-11-28 09:51:44 -05:00
|
|
|
stop();
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (folderPushers) {
|
2010-05-09 11:27:41 -04:00
|
|
|
setLastRefresh(System.currentTimeMillis());
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String folderName : folderNames) {
|
2009-11-28 09:51:44 -05:00
|
|
|
ImapFolderPusher pusher = folderPushers.get(folderName);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (pusher == null) {
|
2009-11-28 09:51:44 -05:00
|
|
|
pusher = new ImapFolderPusher(mStore, folderName, mReceiver);
|
|
|
|
folderPushers.put(folderName, pusher);
|
|
|
|
pusher.start();
|
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void refresh() {
|
|
|
|
synchronized (folderPushers) {
|
|
|
|
for (ImapFolderPusher folderPusher : folderPushers.values()) {
|
|
|
|
try {
|
2009-11-28 09:51:44 -05:00
|
|
|
folderPusher.refresh();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e);
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-12-06 19:56:06 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void stop() {
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Requested stop of IMAP pusher");
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
synchronized (folderPushers) {
|
|
|
|
for (ImapFolderPusher folderPusher : folderPushers.values()) {
|
|
|
|
try {
|
2010-01-02 21:00:20 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Requesting stop of IMAP folderPusher " + folderPusher.getName());
|
2009-11-28 09:51:44 -05:00
|
|
|
folderPusher.stop();
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Got exception while stopping " + folderPusher.getName(), e);
|
2009-11-28 09:51:44 -05:00
|
|
|
}
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-28 09:51:44 -05:00
|
|
|
folderPushers.clear();
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getRefreshInterval() {
|
2010-05-09 11:27:41 -04:00
|
|
|
return (getAccount().getIdleRefreshMinutes() * 60 * 1000);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public long getLastRefresh() {
|
2010-05-09 11:27:41 -04:00
|
|
|
return lastRefresh;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void setLastRefresh(long lastRefresh) {
|
2010-05-09 11:27:41 -04:00
|
|
|
this.lastRefresh = lastRefresh;
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
private interface UntaggedHandler {
|
2009-10-21 20:41:06 -04:00
|
|
|
void handleAsyncUntaggedResponse(ImapResponse respose);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected static class ImapPushState {
|
2009-10-21 20:41:06 -04:00
|
|
|
protected int uidNext;
|
2011-02-06 17:09:48 -05:00
|
|
|
protected ImapPushState(int nUidNext) {
|
2009-10-21 20:41:06 -04:00
|
|
|
uidNext = nUidNext;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
protected static ImapPushState parse(String pushState) {
|
2009-10-21 20:41:06 -04:00
|
|
|
int newUidNext = -1;
|
2011-02-06 17:09:48 -05:00
|
|
|
if (pushState != null) {
|
2009-10-21 20:41:06 -04:00
|
|
|
StringTokenizer tokenizer = new StringTokenizer(pushState, ";");
|
2011-02-06 17:09:48 -05:00
|
|
|
while (tokenizer.hasMoreTokens()) {
|
2009-10-21 20:41:06 -04:00
|
|
|
StringTokenizer thisState = new StringTokenizer(tokenizer.nextToken(), "=");
|
2011-02-06 17:09:48 -05:00
|
|
|
if (thisState.hasMoreTokens()) {
|
2009-10-21 20:41:06 -04:00
|
|
|
String key = thisState.nextToken();
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if ("uidNext".equalsIgnoreCase(key) && thisState.hasMoreTokens()) {
|
2009-10-21 20:41:06 -04:00
|
|
|
String value = thisState.nextToken();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2009-10-21 20:41:06 -04:00
|
|
|
newUidNext = Integer.parseInt(value);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2009-12-14 21:50:53 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to part uidNext value " + value, e);
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new ImapPushState(newUidNext);
|
|
|
|
}
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String toString() {
|
2009-10-21 20:41:06 -04:00
|
|
|
return "uidNext=" + uidNext;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2009-10-21 20:41:06 -04:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
private interface ImapSearcher {
|
2009-11-26 00:10:12 -05:00
|
|
|
List<ImapResponse> search() throws IOException, MessagingException;
|
|
|
|
}
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private static class FetchBodyCallback implements ImapResponseParser.IImapResponseCallback {
|
2010-05-19 09:31:48 -04:00
|
|
|
private HashMap<String, Message> mMessageMap;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
FetchBodyCallback(HashMap<String, Message> mesageMap) {
|
2010-05-30 00:17:00 -04:00
|
|
|
mMessageMap = mesageMap;
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object foundLiteral(ImapResponse response,
|
2011-02-06 17:09:48 -05:00
|
|
|
FixedLengthInputStream literal) throws IOException, Exception {
|
2010-05-19 09:31:48 -04:00
|
|
|
if (response.mTag == null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
|
|
|
|
String uid = fetchList.getKeyedString("UID");
|
|
|
|
|
|
|
|
ImapMessage message = (ImapMessage) mMessageMap.get(uid);
|
|
|
|
message.parse(literal);
|
|
|
|
|
|
|
|
// Return placeholder object
|
|
|
|
return new Integer(1);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private static class FetchPartCallback implements ImapResponseParser.IImapResponseCallback {
|
2010-05-19 09:31:48 -04:00
|
|
|
private Part mPart;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
FetchPartCallback(Part part) {
|
2010-05-19 09:31:48 -04:00
|
|
|
mPart = part;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object foundLiteral(ImapResponse response,
|
2011-02-06 17:09:48 -05:00
|
|
|
FixedLengthInputStream literal) throws IOException, Exception {
|
2010-05-19 09:31:48 -04:00
|
|
|
if (response.mTag == null &&
|
2011-02-06 17:09:48 -05:00
|
|
|
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
|
2010-05-19 09:31:48 -04:00
|
|
|
//TODO: check for correct UID
|
|
|
|
|
|
|
|
String contentTransferEncoding = mPart.getHeader(
|
2010-05-30 00:17:00 -04:00
|
|
|
MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING)[0];
|
2010-05-19 09:31:48 -04:00
|
|
|
|
|
|
|
return MimeUtility.decodeBody(literal, contentTransferEncoding);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|