Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --- ./Pop3Store.java.ORIG 2010-08-24 08:35:44.831443463 -0400
- +++ Pop3Store.java 2010-08-24 10:06:34.803443702 -0400
- @@ -11,13 +11,22 @@
- import com.fsck.k9.mail.Folder.OpenMode;
- import com.fsck.k9.mail.internet.MimeMessage;
- +import org.apache.commons.codec.binary.Base64;
- +import org.apache.commons.codec.binary.Hex;
- +
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLException;
- import javax.net.ssl.TrustManager;
- import java.io.*;
- import java.net.*;
- +import java.nio.ByteBuffer;
- +import java.nio.CharBuffer;
- +import java.nio.charset.Charset;
- import java.security.GeneralSecurityException;
- +import java.security.MessageDigest;
- +import java.security.NoSuchAlgorithmException;
- import java.security.SecureRandom;
- +import java.security.Security;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.LinkedList;
- @@ -33,13 +42,19 @@
- public static final int CONNECTION_SECURITY_SSL_REQUIRED = 3;
- public static final int CONNECTION_SECURITY_SSL_OPTIONAL = 4;
- + private enum AuthType { PLAIN, CRAM_MD5 };
- +
- private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED };
- + private static final String CAPABILITY_CAPABILITY = "CAPABILITY";
- + private static final String COMMAND_CAPABILITY = "CAPABILITY";
- +
- private String mHost;
- private int mPort;
- private String mUsername;
- private String mPassword;
- private int mConnectionSecurity;
- + private AuthType mAuthType;
- private HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
- private Pop3Capabilities mCapabilities;
- @@ -70,10 +85,10 @@
- /**
- * pop3://user:password@server:port CONNECTION_SECURITY_NONE
- - * pop3+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
- - * pop3+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
- - * pop3+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
- - * pop3+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
- + * pop3+tls://auth:user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
- + * pop3+tls+://auth:user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
- + * pop3+ssl+://auth:user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
- + * pop3+ssl://auth:user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
- */
- public Pop3Store(Account account) throws MessagingException
- {
- @@ -132,11 +147,18 @@
- try
- {
- String[] userInfoParts = uri.getUserInfo().split(":");
- - mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
- - if (userInfoParts.length > 1)
- + if (userInfoParts.length == 2)
- {
- + mAuthType = AuthType.PLAIN;
- + mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
- mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
- }
- + else
- + {
- + mAuthType = AuthType.valueOf(userInfoParts[0]);
- + mUsername = URLDecoder.decode(userInfoParts[1], "UTF-8");
- + mPassword = URLDecoder.decode(userInfoParts[2], "UTF-8");
- + }
- }
- catch (UnsupportedEncodingException enc)
- {
- @@ -197,7 +219,7 @@
- private HashMap<Integer, Pop3Message> mMsgNumToMsgMap = new HashMap<Integer, Pop3Message>();
- private HashMap<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>();
- private String mName;
- - private int mMessageCount;
- + protected volatile int mMessageCount;
- public Pop3Folder(String name)
- {
- @@ -217,6 +239,8 @@
- return;
- }
- + boolean authSuccess = false;
- +
- if (!mName.equalsIgnoreCase("INBOX"))
- {
- throw new MessagingException("Folder does not exist");
- @@ -289,8 +313,26 @@
- try
- {
- - executeSimpleCommand("USER " + mUsername);
- - executeSimpleCommand("PASS " + mPassword, true);
- + if (mAuthType == AuthType.CRAM_MD5)
- + {
- + authCramMD5();
- + // The authCramMD5 method called on the previous line does not allow for handling updated capabilities
- + // sent by the server. So, to make sure we update to the post-authentication capability list
- + // we fetch the capabilities here.
- + if (K9.DEBUG)
- + Log.i(K9.LOG_TAG, "Updating capabilities after CRAM-MD5 authentication for " + getLogId());
- + List<ImapResponse> responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
- + if (responses.size() != 2)
- + {
- + throw new MessagingException("Invalid CAPABILITY response received");
- + }
- +
- + }
- + else if (mAuthType == AuthType.PLAIN)
- + {
- + receiveCapabilities(executeSimpleCommand("LOGIN \"" + escapeString(mUsername) + "\" \"" + escapeString(mPassword) + "\"", true));
- + }
- + authSuccess = true;
- }
- catch (MessagingException me)
- {
- @@ -312,14 +354,109 @@
- {
- throw new MessagingException("Unable to open connection to POP server.", ioe);
- }
- + }
- + finally
- + {
- + if (authSuccess == true)
- + {
- + String response = executeSimpleCommand("STAT");
- + String[] parts = response.split(" ");
- + mMessageCount = Integer.parseInt(parts[1]);
- +
- + mUidToMsgMap.clear();
- + mMsgNumToMsgMap.clear();
- + mUidToMsgNumMap.clear();
- + }
- + else
- + }
- + Log.e(K9.LOG_TAG, "Failed to login, closing connection for " + getLogId());
- + close();
- + }
- + }
- + }
- - String response = executeSimpleCommand("STAT");
- - String[] parts = response.split(" ");
- - mMessageCount = Integer.parseInt(parts[1]);
- -
- - mUidToMsgMap.clear();
- - mMsgNumToMsgMap.clear();
- - mUidToMsgNumMap.clear();
- + protected void authCramMD5() throws AuthenticationFailedException, MessagingException
- + {
- + try
- + {
- + String tag = sendCommand("AUTHENTICATE CRAM-MD5", false);
- + byte[] buf = new byte[ 1024 ];
- + int b64NonceLen = 0;
- + for (int i = 0; i < buf.length; i++)
- + {
- + buf[ i ] = (byte)mIn.read();
- + if (buf[i] == 0x0a)
- + {
- + b64NonceLen = i;
- + break;
- + }
- + }
- + if (b64NonceLen == 0)
- + {
- + throw new AuthenticationFailedException("Error negotiating CRAM-MD5: nonce too long.");
- + }
- + byte[] b64NonceTrim = new byte[ b64NonceLen - 2 ];
- + System.arraycopy(buf, 1, b64NonceTrim, 0, b64NonceLen - 2);
- + byte[] nonce = Base64.decodeBase64(b64NonceTrim);
- + if (K9.DEBUG)
- + {
- + Log.d(K9.LOG_TAG, "Got nonce: " + new String(b64NonceTrim, "US-ASCII"));
- + Log.d(K9.LOG_TAG, "Plaintext nonce: " + new String(nonce, "US-ASCII"));
- + }
- +
- + byte[] ipad = new byte[64];
- + byte[] opad = new byte[64];
- + byte[] secretBytes = mPassword.getBytes("US-ASCII");
- + MessageDigest md = MessageDigest.getInstance("MD5");
- + if (secretBytes.length > 64)
- + {
- + secretBytes = md.digest(secretBytes);
- + }
- + System.arraycopy(secretBytes, 0, ipad, 0, secretBytes.length);
- + System.arraycopy(secretBytes, 0, opad, 0, secretBytes.length);
- + for (int i = 0; i < ipad.length; i++) ipad[i] ^= 0x36;
- + for (int i = 0; i < opad.length; i++) opad[i] ^= 0x5c;
- + md.update(ipad);
- + byte[] firstPass = md.digest(nonce);
- + md.update(opad);
- + byte[] result = md.digest(firstPass);
- + String plainCRAM = mUsername + " " + new String(Hex.encodeHex(result));
- + byte[] b64CRAM = Base64.encodeBase64(plainCRAM.getBytes("US-ASCII"));
- + if (K9.DEBUG)
- + {
- + Log.d(K9.LOG_TAG, "Username == " + mUsername);
- + Log.d(K9.LOG_TAG, "plainCRAM: " + plainCRAM);
- + Log.d(K9.LOG_TAG, "b64CRAM: " + new String(b64CRAM, "US-ASCII"));
- + }
- +
- + mOut.write(b64CRAM);
- + mOut.write(new byte[] { 0x0d, 0x0a });
- + mOut.flush();
- + int respLen = 0;
- + for (int i = 0; i < buf.length; i++)
- + {
- + buf[ i ] = (byte)mIn.read();
- + if (buf[i] == 0x0a)
- + {
- + respLen = i;
- + break;
- + }
- + }
- + String toMatch = tag + " OK";
- + String respStr = new String(buf, 0, respLen);
- + if (!respStr.startsWith(toMatch))
- + {
- + throw new AuthenticationFailedException("CRAM-MD5 error: " + respStr);
- + }
- + }
- + catch (IOException ioe)
- + {
- + throw new AuthenticationFailedException("CRAM-MD5 Auth Failed.");
- + }
- + catch (NoSuchAlgorithmException nsae)
- + {
- + throw new AuthenticationFailedException("MD5 Not Available.");
- + }
- }
- @Override
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement