diff --git a/src/android/net/http/DomainNameChecker.java b/src/android/net/http/DomainNameChecker.java deleted file mode 100644 index 46936dfed..000000000 --- a/src/android/net/http/DomainNameChecker.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import org.bouncycastle.asn1.x509.X509Name; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.cert.X509Certificate; -import java.security.cert.CertificateParsingException; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; -import java.util.Vector; - -/** - * Implements basic domain-name validation as specified by RFC2818. - * - * {@hide} - */ -public class DomainNameChecker { - private static Pattern QUICK_IP_PATTERN; - static { - try { - QUICK_IP_PATTERN = Pattern.compile("^[a-f0-9\\.:]+$"); - } catch (PatternSyntaxException e) {} - } - - private static final int ALT_DNS_NAME = 2; - private static final int ALT_IPA_NAME = 7; - - /** - * Checks the site certificate against the domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - public static boolean match(X509Certificate certificate, String thisDomain) { - if (certificate == null || thisDomain == null || thisDomain.length() == 0) { - return false; - } - - thisDomain = thisDomain.toLowerCase(); - if (!isIpAddress(thisDomain)) { - return matchDns(certificate, thisDomain); - } else { - return matchIpAddress(certificate, thisDomain); - } - } - - /** - * @return True iff the domain name is specified as an IP address - */ - private static boolean isIpAddress(String domain) { - boolean rval = (domain != null && domain.length() != 0); - if (rval) { - try { - // do a quick-dirty IP match first to avoid DNS lookup - rval = QUICK_IP_PATTERN.matcher(domain).matches(); - if (rval) { - rval = domain.equals( - InetAddress.getByName(domain).getHostAddress()); - } - } catch (UnknownHostException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "unknown host exception"; - } - - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.isIpAddress(): " + errorMessage); - } - - rval = false; - } - } - - return rval; - } - - /** - * Checks the site certificate against the IP domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The DNS domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - private static boolean matchIpAddress(X509Certificate certificate, String thisDomain) { - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchIpAddress(): this domain: " + thisDomain); - } - - try { - Collection subjectAltNames = certificate.getSubjectAlternativeNames(); - if (subjectAltNames != null) { - Iterator i = subjectAltNames.iterator(); - while (i.hasNext()) { - List altNameEntry = (List)(i.next()); - if (altNameEntry != null && 2 <= altNameEntry.size()) { - Integer altNameType = (Integer)(altNameEntry.get(0)); - if (altNameType != null) { - if (altNameType.intValue() == ALT_IPA_NAME) { - String altName = (String)(altNameEntry.get(1)); - if (altName != null) { - if (HttpLog.LOGV) { - HttpLog.v("alternative IP: " + altName); - } - if (thisDomain.equalsIgnoreCase(altName)) { - return true; - } - } - } - } - } - } - } - } catch (CertificateParsingException e) {} - - return false; - } - - /** - * Checks the site certificate against the DNS domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The DNS domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - private static boolean matchDns(X509Certificate certificate, String thisDomain) { - boolean hasDns = false; - try { - Collection subjectAltNames = certificate.getSubjectAlternativeNames(); - if (subjectAltNames != null) { - Iterator i = subjectAltNames.iterator(); - while (i.hasNext()) { - List altNameEntry = (List)(i.next()); - if (altNameEntry != null && 2 <= altNameEntry.size()) { - Integer altNameType = (Integer)(altNameEntry.get(0)); - if (altNameType != null) { - if (altNameType.intValue() == ALT_DNS_NAME) { - hasDns = true; - String altName = (String)(altNameEntry.get(1)); - if (altName != null) { - if (matchDns(thisDomain, altName)) { - return true; - } - } - } - } - } - } - } - } catch (CertificateParsingException e) { - // one way we can get here is if an alternative name starts with - // '*' character, which is contrary to one interpretation of the - // spec (a valid DNS name must start with a letter); there is no - // good way around this, and in order to be compatible we proceed - // to check the common name (ie, ignore alternative names) - if (HttpLog.LOGV) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "failed to parse certificate"; - } - - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchDns(): " + errorMessage); - } - } - } - - if (!hasDns) { - X509Name xName = new X509Name(certificate.getSubjectDN().getName()); - Vector val = xName.getValues(); - Vector oid = xName.getOIDs(); - for (int i = 0; i < oid.size(); i++) { - if (oid.elementAt(i).equals(X509Name.CN)) { - return matchDns(thisDomain, (String)(val.elementAt(i))); - } - } - } - - return false; - } - - /** - * @param thisDomain The domain name of the site being visited - * @param thatDomain The domain name from the certificate - * @return True iff thisDomain matches thatDomain as specified by RFC2818 - */ - private static boolean matchDns(String thisDomain, String thatDomain) { - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchDns():" + - " this domain: " + thisDomain + - " that domain: " + thatDomain); - } - - if (thisDomain == null || thisDomain.length() == 0 || - thatDomain == null || thatDomain.length() == 0) { - return false; - } - - thatDomain = thatDomain.toLowerCase(); - - // (a) domain name strings are equal, ignoring case: X matches X - boolean rval = thisDomain.equals(thatDomain); - if (!rval) { - String[] thisDomainTokens = thisDomain.split("\\."); - String[] thatDomainTokens = thatDomain.split("\\."); - - int thisDomainTokensNum = thisDomainTokens.length; - int thatDomainTokensNum = thatDomainTokens.length; - - // (b) OR thatHost is a '.'-suffix of thisHost: Z.Y.X matches X - if (thisDomainTokensNum >= thatDomainTokensNum) { - for (int i = thatDomainTokensNum - 1; i >= 0; --i) { - rval = thisDomainTokens[i].equals(thatDomainTokens[i]); - if (!rval) { - // (c) OR we have a special *-match: - // Z.Y.X matches *.Y.X but does not match *.X - rval = (i == 0 && thisDomainTokensNum == thatDomainTokensNum); - if (rval) { - rval = thatDomainTokens[0].equals("*"); - if (!rval) { - // (d) OR we have a *-component match: - // f*.com matches foo.com but not bar.com - rval = domainTokenMatch( - thisDomainTokens[0], thatDomainTokens[0]); - } - } - - break; - } - } - } - } - - return rval; - } - - /** - * @param thisDomainToken The domain token from the current domain name - * @param thatDomainToken The domain token from the certificate - * @return True iff thisDomainToken matches thatDomainToken, using the - * wildcard match as specified by RFC2818-3.1. For example, f*.com must - * match foo.com but not bar.com - */ - private static boolean domainTokenMatch(String thisDomainToken, String thatDomainToken) { - if (thisDomainToken != null && thatDomainToken != null) { - int starIndex = thatDomainToken.indexOf('*'); - if (starIndex >= 0) { - if (thatDomainToken.length() - 1 <= thisDomainToken.length()) { - String prefix = thatDomainToken.substring(0, starIndex); - String suffix = thatDomainToken.substring(starIndex + 1); - - return thisDomainToken.startsWith(prefix) && thisDomainToken.endsWith(suffix); - } - } - } - - return false; - } -} diff --git a/src/android/net/http/HttpLog.java b/src/android/net/http/HttpLog.java deleted file mode 100644 index 30bf64754..000000000 --- a/src/android/net/http/HttpLog.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * package-level logging flag - */ - -package android.net.http; - -import android.os.SystemClock; - -import android.util.Log; -import android.util.Config; - -/** - * {@hide} - */ -class HttpLog { - private final static String LOGTAG = "http"; - - private static final boolean DEBUG = false; - static final boolean LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - static void v(String logMe) { - Log.v(LOGTAG, SystemClock.uptimeMillis() + " " + Thread.currentThread().getName() + " " + logMe); - } - - static void e(String logMe) { - Log.e(LOGTAG, logMe); - } -} diff --git a/src/com/fsck/k9/EmailAddressValidator.java b/src/com/fsck/k9/EmailAddressValidator.java index 054db6317..052615621 100644 --- a/src/com/fsck/k9/EmailAddressValidator.java +++ b/src/com/fsck/k9/EmailAddressValidator.java @@ -18,6 +18,6 @@ public class EmailAddressValidator implements Validator public boolean isValidAddressOnly(CharSequence text) { - return android.text.util.Regex.EMAIL_ADDRESS_PATTERN.matcher(text).matches(); + return com.fsck.k9.helper.Regex.EMAIL_ADDRESS_PATTERN.matcher(text).matches(); } } diff --git a/src/com/fsck/k9/helper/DomainNameChecker.java b/src/com/fsck/k9/helper/DomainNameChecker.java new file mode 100644 index 000000000..36346e68a --- /dev/null +++ b/src/com/fsck/k9/helper/DomainNameChecker.java @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fsck.k9.helper; + +import org.bouncycastle.asn1.x509.X509Name; +import android.util.Log; +import com.fsck.k9.K9; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateParsingException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import java.util.Vector; + +/** + * Implements basic domain-name validation as specified by RFC2818. + */ +public class DomainNameChecker +{ + private static Pattern QUICK_IP_PATTERN; + static + { + try + { + QUICK_IP_PATTERN = Pattern.compile("^[a-f0-9\\.:]+$"); + } + catch (PatternSyntaxException e) + { + } + } + + private static final int ALT_DNS_NAME = 2; + private static final int ALT_IPA_NAME = 7; + + /** + * Checks the site certificate against the domain name of the site being + * visited + * + * @param certificate + * The certificate to check + * @param thisDomain + * The domain name of the site being visited + * @return True iff if there is a domain match as specified by RFC2818 + */ + public static boolean match(X509Certificate certificate, String thisDomain) + { + if ((certificate == null) || (thisDomain == null) + || (thisDomain.length() == 0)) + { + return false; + } + + thisDomain = thisDomain.toLowerCase(); + if (!isIpAddress(thisDomain)) + { + return matchDns(certificate, thisDomain); + } + else + { + return matchIpAddress(certificate, thisDomain); + } + } + + /** + * @return True iff the domain name is specified as an IP address + */ + @SuppressWarnings("null") + private static boolean isIpAddress(String domain) + { + boolean rval = ((domain != null) && (domain.length() != 0)); + if (rval) + { + try + { + // do a quick-dirty IP match first to avoid DNS lookup + rval = QUICK_IP_PATTERN.matcher(domain).matches(); + if (rval) + { + rval = domain.equals(InetAddress.getByName(domain) + .getHostAddress()); + } + } + catch (UnknownHostException e) + { + String errorMessage = e.getMessage(); + if (errorMessage == null) + { + errorMessage = "unknown host exception"; + } + + if (K9.DEBUG) + { + Log.v(K9.LOG_TAG, "DomainNameChecker.isIpAddress(): " + + errorMessage); + } + + rval = false; + } + } + + return rval; + } + + /** + * Checks the site certificate against the IP domain name of the site being + * visited + * + * @param certificate + * The certificate to check + * @param thisDomain + * The DNS domain name of the site being visited + * @return True iff if there is a domain match as specified by RFC2818 + */ + private static boolean matchIpAddress(X509Certificate certificate, String thisDomain) + { + if (K9.DEBUG) + { + Log.v(K9.LOG_TAG, "DomainNameChecker.matchIpAddress(): this domain: " + thisDomain); + } + + try + { + Collection subjectAltNames = certificate.getSubjectAlternativeNames(); + if (subjectAltNames != null) + { + Iterator i = subjectAltNames.iterator(); + while (i.hasNext()) + { + List altNameEntry = (List) (i.next()); + if ((altNameEntry != null) && (2 <= altNameEntry.size())) + { + Integer altNameType = (Integer) (altNameEntry.get(0)); + if (altNameType != null) + { + if (altNameType.intValue() == ALT_IPA_NAME) + { + String altName = (String) (altNameEntry.get(1)); + if (altName != null) + { + if (K9.DEBUG) + { + Log.v(K9.LOG_TAG, "alternative IP: " + altName); + } + if (thisDomain.equalsIgnoreCase(altName)) + { + return true; + } + } + } + } + } + } + } + } + catch (CertificateParsingException e) + { + } + + return false; + } + + /** + * Checks the site certificate against the DNS domain name of the site being + * visited + * + * @param certificate + * The certificate to check + * @param thisDomain + * The DNS domain name of the site being visited + * @return True iff if there is a domain match as specified by RFC2818 + */ + private static boolean matchDns(X509Certificate certificate, String thisDomain) + { + boolean hasDns = false; + try + { + Collection subjectAltNames = certificate.getSubjectAlternativeNames(); + if (subjectAltNames != null) + { + Iterator i = subjectAltNames.iterator(); + while (i.hasNext()) + { + List altNameEntry = (List) (i.next()); + if ((altNameEntry != null) && (2 <= altNameEntry.size())) + { + Integer altNameType = (Integer) (altNameEntry.get(0)); + if (altNameType != null) + { + if (altNameType.intValue() == ALT_DNS_NAME) + { + hasDns = true; + String altName = (String) (altNameEntry.get(1)); + if (altName != null) + { + if (matchDns(thisDomain, altName)) + { + return true; + } + } + } + } + } + } + } + } + catch (CertificateParsingException e) + { + // one way we can get here is if an alternative name starts with + // '*' character, which is contrary to one interpretation of the + // spec (a valid DNS name must start with a letter); there is no + // good way around this, and in order to be compatible we proceed + // to check the common name (ie, ignore alternative names) + if (K9.DEBUG) + { + String errorMessage = e.getMessage(); + if (errorMessage == null) + { + errorMessage = "failed to parse certificate"; + } + + Log.v(K9.LOG_TAG, "DomainNameChecker.matchDns(): " + + errorMessage); + } + } + + if (!hasDns) + { + X509Name xName = new X509Name(certificate.getSubjectDN().getName()); + Vector val = xName.getValues(); + Vector oid = xName.getOIDs(); + for (int i = 0; i < oid.size(); i++) + { + if (oid.elementAt(i).equals(X509Name.CN)) + { + return matchDns(thisDomain, (String) (val.elementAt(i))); + } + } + } + + return false; + } + + /** + * @param thisDomain + * The domain name of the site being visited + * @param thatDomain + * The domain name from the certificate + * @return True iff thisDomain matches thatDomain as specified by RFC2818 + */ + private static boolean matchDns(String thisDomain, String thatDomain) + { + if (K9.DEBUG) + { + Log.v(K9.LOG_TAG, "DomainNameChecker.matchDns():" + + " this domain: " + thisDomain + " that domain: " + + thatDomain); + } + + if ((thisDomain == null) || (thisDomain.length() == 0) + || (thatDomain == null) || (thatDomain.length() == 0)) + { + return false; + } + + thatDomain = thatDomain.toLowerCase(); + + // (a) domain name strings are equal, ignoring case: X matches X + boolean rval = thisDomain.equals(thatDomain); + if (!rval) + { + String[] thisDomainTokens = thisDomain.split("\\."); + String[] thatDomainTokens = thatDomain.split("\\."); + + int thisDomainTokensNum = thisDomainTokens.length; + int thatDomainTokensNum = thatDomainTokens.length; + + // (b) OR thatHost is a '.'-suffix of thisHost: Z.Y.X matches X + if (thisDomainTokensNum >= thatDomainTokensNum) + { + for (int i = thatDomainTokensNum - 1; i >= 0; --i) + { + rval = thisDomainTokens[i].equals(thatDomainTokens[i]); + if (!rval) + { + // (c) OR we have a special *-match: + // Z.Y.X matches *.Y.X but does not match *.X + rval = ((i == 0) && (thisDomainTokensNum == thatDomainTokensNum)); + if (rval) + { + rval = thatDomainTokens[0].equals("*"); + if (!rval) + { + // (d) OR we have a *-component match: + // f*.com matches foo.com but not bar.com + rval = domainTokenMatch(thisDomainTokens[0], + thatDomainTokens[0]); + } + } + + break; + } + } + } + } + + return rval; + } + + /** + * @param thisDomainToken + * The domain token from the current domain name + * @param thatDomainToken + * The domain token from the certificate + * @return True iff thisDomainToken matches thatDomainToken, using the + * wildcard match as specified by RFC2818-3.1. For example, f*.com + * must match foo.com but not bar.com + */ + private static boolean domainTokenMatch(String thisDomainToken, String thatDomainToken) + { + if ((thisDomainToken != null) && (thatDomainToken != null)) + { + int starIndex = thatDomainToken.indexOf('*'); + if (starIndex >= 0) + { + if (thatDomainToken.length() - 1 <= thisDomainToken.length()) + { + String prefix = thatDomainToken.substring(0, starIndex); + String suffix = thatDomainToken.substring(starIndex + 1); + + return thisDomainToken.startsWith(prefix) + && thisDomainToken.endsWith(suffix); + } + } + } + + return false; + } +} diff --git a/src/android/text/util/Regex.java b/src/com/fsck/k9/helper/Regex.java similarity index 96% rename from src/android/text/util/Regex.java rename to src/com/fsck/k9/helper/Regex.java index badff5a97..799134b82 100644 --- a/src/android/text/util/Regex.java +++ b/src/com/fsck/k9/helper/Regex.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package android.text.util; +package com.fsck.k9.helper; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * @hide - */ -public class Regex { +public class Regex +{ /** * Regular expression pattern to match all IANA top-level domains. * List accurate as of 2007/06/15. List taken from: @@ -161,16 +159,19 @@ public class Regex { * @return A String comprising all of the non-null matched * groups concatenated together */ - public static final String concatGroups(Matcher matcher) { + public static final String concatGroups(Matcher matcher) + { StringBuilder b = new StringBuilder(); final int numGroups = matcher.groupCount(); - for (int i = 1; i <= numGroups; i++) { + for (int i = 1; i <= numGroups; i++) + { String s = matcher.group(i); System.err.println("Group(" + i + ") : " + s); - if (s != null) { + if (s != null) + { b.append(s); } } @@ -188,14 +189,17 @@ public class Regex { * @return A String comprising all of the digits and plus in * the match */ - public static final String digitsAndPlusOnly(Matcher matcher) { + public static final String digitsAndPlusOnly(Matcher matcher) + { StringBuilder buffer = new StringBuilder(); String matchingRegion = matcher.group(); - for (int i = 0, size = matchingRegion.length(); i < size; i++) { + for (int i = 0, size = matchingRegion.length(); i < size; i++) + { char character = matchingRegion.charAt(i); - if (character == '+' || Character.isDigit(character)) { + if (character == '+' || Character.isDigit(character)) + { buffer.append(character); } } diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 144c704f8..65430dccd 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -8,13 +8,13 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.net.Uri; -import android.text.util.Regex; import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.controller.MessageRemovalListener; import com.fsck.k9.controller.MessageRetrievalListener; +import com.fsck.k9.helper.Regex; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; diff --git a/src/com/fsck/k9/mail/store/TrustManagerFactory.java b/src/com/fsck/k9/mail/store/TrustManagerFactory.java index 34f066864..3e029b9a3 100644 --- a/src/com/fsck/k9/mail/store/TrustManagerFactory.java +++ b/src/com/fsck/k9/mail/store/TrustManagerFactory.java @@ -3,9 +3,9 @@ package com.fsck.k9.mail.store; import android.app.Application; import android.content.Context; -import android.net.http.DomainNameChecker; import android.util.Log; import com.fsck.k9.K9; +import com.fsck.k9.helper.DomainNameChecker; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager;