Loading k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java +19 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,9 @@ package com.fsck.k9.mail.internet; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; import android.support.annotation.NonNull; Loading @@ -15,7 +17,6 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.apache.james.mime4j.util.MimeUtil; Loading Loading @@ -953,6 +954,23 @@ public class MimeUtility { return null; } public static Map<String,String> getAllHeaderParameters(String headerValue) { Map<String,String> result = new HashMap<>(); headerValue = headerValue.replaceAll("\r|\n", ""); String[] parts = headerValue.split(";"); for (String part : parts) { String[] partParts = part.split("=", 2); if (partParts.length == 2) { String parameterName = partParts[0].trim().toLowerCase(Locale.US); String parameterValue = partParts[1].trim(); result.put(parameterName, parameterValue); } } return result; } public static Part findFirstPartByMimeType(Part part, String mimeType) { if (part.getBody() instanceof Multipart) { Multipart multipart = (Multipart)part.getBody(); Loading k9mail/src/main/java/com/fsck/k9/activity/MessageLoaderHelper.java +3 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import com.fsck.k9.autocrypt.AutocryptOperations; import com.fsck.k9.ui.crypto.OpenPgpApiFactory; import timber.log.Timber; Loading Loading @@ -270,7 +271,8 @@ public class MessageLoaderHelper { messageCryptoHelper = retainCryptoHelperFragment.getData(); } if (messageCryptoHelper == null || messageCryptoHelper.isConfiguredForOutdatedCryptoProvider()) { messageCryptoHelper = new MessageCryptoHelper(context, new OpenPgpApiFactory()); messageCryptoHelper = new MessageCryptoHelper( context, new OpenPgpApiFactory(), AutocryptOperations.getInstance()); retainCryptoHelperFragment.setData(messageCryptoHelper); } messageCryptoHelper.asyncStartOrResumeProcessingMessage( Loading k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptHeader.java 0 → 100644 +31 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.Map; class AutocryptHeader { static final String AUTOCRYPT_HEADER = "Autocrypt"; static final String AUTOCRYPT_PARAM_TO = "addr"; static final String AUTOCRYPT_PARAM_KEY_DATA = "keydata"; static final String AUTOCRYPT_PARAM_TYPE = "type"; static final String AUTOCRYPT_TYPE_1 = "1"; static final String AUTOCRYPT_PARAM_PREFER_ENCRYPT = "prefer-encrypt"; static final String AUTOCRYPT_PREFER_ENCRYPT_MUTUAL = "mutual"; final byte[] keyData; final String addr; final Map<String,String> parameters; final boolean isPreferEncryptMutual; AutocryptHeader(Map<String, String> parameters, String addr, byte[] keyData, boolean isPreferEncryptMutual) { this.parameters = parameters; this.addr = addr; this.keyData = keyData; this.isPreferEncryptMutual = isPreferEncryptMutual; } } k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptHeaderParser.java 0 → 100644 +97 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.ArrayList; import java.util.Map; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.internet.MimeUtility; import okio.ByteString; import timber.log.Timber; class AutocryptHeaderParser { private static final AutocryptHeaderParser INSTANCE = new AutocryptHeaderParser(); public static AutocryptHeaderParser getInstance() { return INSTANCE; } private AutocryptHeaderParser() { } @Nullable AutocryptHeader getValidAutocryptHeader(Message currentMessage) { String[] headers = currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER); ArrayList<AutocryptHeader> autocryptHeaders = parseAllAutocryptHeaders(headers); boolean isSingleValidHeader = autocryptHeaders.size() == 1; return isSingleValidHeader ? autocryptHeaders.get(0) : null; } @Nullable private AutocryptHeader parseAutocryptHeader(String headerValue) { Map<String,String> parameters = MimeUtility.getAllHeaderParameters(headerValue); String type = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TYPE); if (type != null && !type.equals(AutocryptHeader.AUTOCRYPT_TYPE_1)) { Timber.e("autocrypt: unsupported type parameter %s", type); return null; } String base64KeyData = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_KEY_DATA); if (base64KeyData == null) { Timber.e("autocrypt: missing key parameter"); return null; } ByteString byteString = ByteString.decodeBase64(base64KeyData); if (byteString == null) { Timber.e("autocrypt: error parsing base64 data"); return null; } String to = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TO); if (to == null) { Timber.e("autocrypt: no to header!"); return null; } boolean isPreferEncryptMutual = false; String preferEncrypt = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_PREFER_ENCRYPT); if (AutocryptHeader.AUTOCRYPT_PREFER_ENCRYPT_MUTUAL.equalsIgnoreCase(preferEncrypt)) { isPreferEncryptMutual = true; } if (hasCriticalParameters(parameters)) { return null; } return new AutocryptHeader(parameters, to, byteString.toByteArray(), isPreferEncryptMutual); } private boolean hasCriticalParameters(Map<String, String> parameters) { for (String parameterName : parameters.keySet()) { if (parameterName != null && !parameterName.startsWith("_")) { return true; } } return false; } @NonNull private ArrayList<AutocryptHeader> parseAllAutocryptHeaders(String[] headers) { ArrayList<AutocryptHeader> autocryptHeaders = new ArrayList<>(); for (String header : headers) { AutocryptHeader autocryptHeader = parseAutocryptHeader(header); if (autocryptHeader != null) { autocryptHeaders.add(autocryptHeader); } } return autocryptHeaders; } } k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptOperations.java 0 → 100644 +52 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.Date; import android.content.Intent; import com.fsck.k9.mail.Message; import org.openintents.openpgp.AutocryptPeerUpdate; import org.openintents.openpgp.util.OpenPgpApi; public class AutocryptOperations { private final AutocryptHeaderParser autocryptHeaderParser; public static AutocryptOperations getInstance() { AutocryptHeaderParser autocryptHeaderParser = AutocryptHeaderParser.getInstance(); return new AutocryptOperations(autocryptHeaderParser); } private AutocryptOperations(AutocryptHeaderParser autocryptHeaderParser) { this.autocryptHeaderParser = autocryptHeaderParser; } public boolean addAutocryptPeerUpdateToIntentIfPresent(Message currentMessage, Intent intent) { AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(currentMessage); if (autocryptHeader == null) { return false; } String messageFromAddress = currentMessage.getFrom()[0].getAddress(); if (!autocryptHeader.addr.equalsIgnoreCase(messageFromAddress)) { return false; } Date messageDate = currentMessage.getSentDate(); Date internalDate = currentMessage.getInternalDate(); Date effectiveDate = messageDate.before(internalDate) ? messageDate : internalDate; AutocryptPeerUpdate data = AutocryptPeerUpdate.create( autocryptHeader.keyData, effectiveDate, autocryptHeader.isPreferEncryptMutual); intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, messageFromAddress); intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE, data); return true; } public boolean hasAutocryptHeader(Message currentMessage) { return currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER).length > 0; } } Loading
k9mail-library/src/main/java/com/fsck/k9/mail/internet/MimeUtility.java +19 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,9 @@ package com.fsck.k9.mail.internet; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; import android.support.annotation.NonNull; Loading @@ -15,7 +17,6 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.apache.james.mime4j.util.MimeUtil; Loading Loading @@ -953,6 +954,23 @@ public class MimeUtility { return null; } public static Map<String,String> getAllHeaderParameters(String headerValue) { Map<String,String> result = new HashMap<>(); headerValue = headerValue.replaceAll("\r|\n", ""); String[] parts = headerValue.split(";"); for (String part : parts) { String[] partParts = part.split("=", 2); if (partParts.length == 2) { String parameterName = partParts[0].trim().toLowerCase(Locale.US); String parameterValue = partParts[1].trim(); result.put(parameterName, parameterValue); } } return result; } public static Part findFirstPartByMimeType(Part part, String mimeType) { if (part.getBody() instanceof Multipart) { Multipart multipart = (Multipart)part.getBody(); Loading
k9mail/src/main/java/com/fsck/k9/activity/MessageLoaderHelper.java +3 −1 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import com.fsck.k9.autocrypt.AutocryptOperations; import com.fsck.k9.ui.crypto.OpenPgpApiFactory; import timber.log.Timber; Loading Loading @@ -270,7 +271,8 @@ public class MessageLoaderHelper { messageCryptoHelper = retainCryptoHelperFragment.getData(); } if (messageCryptoHelper == null || messageCryptoHelper.isConfiguredForOutdatedCryptoProvider()) { messageCryptoHelper = new MessageCryptoHelper(context, new OpenPgpApiFactory()); messageCryptoHelper = new MessageCryptoHelper( context, new OpenPgpApiFactory(), AutocryptOperations.getInstance()); retainCryptoHelperFragment.setData(messageCryptoHelper); } messageCryptoHelper.asyncStartOrResumeProcessingMessage( Loading
k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptHeader.java 0 → 100644 +31 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.Map; class AutocryptHeader { static final String AUTOCRYPT_HEADER = "Autocrypt"; static final String AUTOCRYPT_PARAM_TO = "addr"; static final String AUTOCRYPT_PARAM_KEY_DATA = "keydata"; static final String AUTOCRYPT_PARAM_TYPE = "type"; static final String AUTOCRYPT_TYPE_1 = "1"; static final String AUTOCRYPT_PARAM_PREFER_ENCRYPT = "prefer-encrypt"; static final String AUTOCRYPT_PREFER_ENCRYPT_MUTUAL = "mutual"; final byte[] keyData; final String addr; final Map<String,String> parameters; final boolean isPreferEncryptMutual; AutocryptHeader(Map<String, String> parameters, String addr, byte[] keyData, boolean isPreferEncryptMutual) { this.parameters = parameters; this.addr = addr; this.keyData = keyData; this.isPreferEncryptMutual = isPreferEncryptMutual; } }
k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptHeaderParser.java 0 → 100644 +97 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.ArrayList; import java.util.Map; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.internet.MimeUtility; import okio.ByteString; import timber.log.Timber; class AutocryptHeaderParser { private static final AutocryptHeaderParser INSTANCE = new AutocryptHeaderParser(); public static AutocryptHeaderParser getInstance() { return INSTANCE; } private AutocryptHeaderParser() { } @Nullable AutocryptHeader getValidAutocryptHeader(Message currentMessage) { String[] headers = currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER); ArrayList<AutocryptHeader> autocryptHeaders = parseAllAutocryptHeaders(headers); boolean isSingleValidHeader = autocryptHeaders.size() == 1; return isSingleValidHeader ? autocryptHeaders.get(0) : null; } @Nullable private AutocryptHeader parseAutocryptHeader(String headerValue) { Map<String,String> parameters = MimeUtility.getAllHeaderParameters(headerValue); String type = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TYPE); if (type != null && !type.equals(AutocryptHeader.AUTOCRYPT_TYPE_1)) { Timber.e("autocrypt: unsupported type parameter %s", type); return null; } String base64KeyData = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_KEY_DATA); if (base64KeyData == null) { Timber.e("autocrypt: missing key parameter"); return null; } ByteString byteString = ByteString.decodeBase64(base64KeyData); if (byteString == null) { Timber.e("autocrypt: error parsing base64 data"); return null; } String to = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TO); if (to == null) { Timber.e("autocrypt: no to header!"); return null; } boolean isPreferEncryptMutual = false; String preferEncrypt = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_PREFER_ENCRYPT); if (AutocryptHeader.AUTOCRYPT_PREFER_ENCRYPT_MUTUAL.equalsIgnoreCase(preferEncrypt)) { isPreferEncryptMutual = true; } if (hasCriticalParameters(parameters)) { return null; } return new AutocryptHeader(parameters, to, byteString.toByteArray(), isPreferEncryptMutual); } private boolean hasCriticalParameters(Map<String, String> parameters) { for (String parameterName : parameters.keySet()) { if (parameterName != null && !parameterName.startsWith("_")) { return true; } } return false; } @NonNull private ArrayList<AutocryptHeader> parseAllAutocryptHeaders(String[] headers) { ArrayList<AutocryptHeader> autocryptHeaders = new ArrayList<>(); for (String header : headers) { AutocryptHeader autocryptHeader = parseAutocryptHeader(header); if (autocryptHeader != null) { autocryptHeaders.add(autocryptHeader); } } return autocryptHeaders; } }
k9mail/src/main/java/com/fsck/k9/autocrypt/AutocryptOperations.java 0 → 100644 +52 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt; import java.util.Date; import android.content.Intent; import com.fsck.k9.mail.Message; import org.openintents.openpgp.AutocryptPeerUpdate; import org.openintents.openpgp.util.OpenPgpApi; public class AutocryptOperations { private final AutocryptHeaderParser autocryptHeaderParser; public static AutocryptOperations getInstance() { AutocryptHeaderParser autocryptHeaderParser = AutocryptHeaderParser.getInstance(); return new AutocryptOperations(autocryptHeaderParser); } private AutocryptOperations(AutocryptHeaderParser autocryptHeaderParser) { this.autocryptHeaderParser = autocryptHeaderParser; } public boolean addAutocryptPeerUpdateToIntentIfPresent(Message currentMessage, Intent intent) { AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(currentMessage); if (autocryptHeader == null) { return false; } String messageFromAddress = currentMessage.getFrom()[0].getAddress(); if (!autocryptHeader.addr.equalsIgnoreCase(messageFromAddress)) { return false; } Date messageDate = currentMessage.getSentDate(); Date internalDate = currentMessage.getInternalDate(); Date effectiveDate = messageDate.before(internalDate) ? messageDate : internalDate; AutocryptPeerUpdate data = AutocryptPeerUpdate.create( autocryptHeader.keyData, effectiveDate, autocryptHeader.isPreferEncryptMutual); intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, messageFromAddress); intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE, data); return true; } public boolean hasAutocryptHeader(Message currentMessage) { return currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER).length > 0; } }