Loading mail/common/src/main/java/com/fsck/k9/mail/Address.java +3 −4 Original line number Diff line number Diff line Loading @@ -7,6 +7,9 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import com.fsck.k9.mail.helper.Rfc822Token; import com.fsck.k9.mail.helper.Rfc822Tokenizer; import com.fsck.k9.mail.helper.TextUtils; import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.codec.DecodeMonitor; import org.apache.james.mime4j.codec.EncoderUtil; Loading @@ -17,10 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.VisibleForTesting; import timber.log.Timber; import android.text.TextUtils; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; public class Address implements Serializable { private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); Loading mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Token.java 0 → 100644 +205 −0 Original line number Diff line number Diff line /* * 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.mail.helper; import org.jetbrains.annotations.Nullable; /** * This class stores an RFC 822-like name, address, and comment, * and provides methods to convert them to quoted strings. */ public class Rfc822Token { @Nullable private String mName, mAddress, mComment; /** * Creates a new Rfc822Token with the specified name, address, * and comment. */ public Rfc822Token(@Nullable String name, @Nullable String address, @Nullable String comment) { mName = name; mAddress = address; mComment = comment; } /** * Returns the name part. */ @Nullable public String getName() { return mName; } /** * Returns the address part. */ @Nullable public String getAddress() { return mAddress; } /** * Returns the comment part. */ @Nullable public String getComment() { return mComment; } /** * Changes the name to the specified name. */ public void setName(@Nullable String name) { mName = name; } /** * Changes the address to the specified address. */ public void setAddress(@Nullable String address) { mAddress = address; } /** * Changes the comment to the specified comment. */ public void setComment(@Nullable String comment) { mComment = comment; } /** * Returns the name (with quoting added if necessary), * the comment (in parentheses), and the address (in angle brackets). * This should be suitable for inclusion in an RFC 822 address list. */ public String toString() { StringBuilder sb = new StringBuilder(); if (mName != null && mName.length() != 0) { sb.append(quoteNameIfNecessary(mName)); sb.append(' '); } if (mComment != null && mComment.length() != 0) { sb.append('('); sb.append(quoteComment(mComment)); sb.append(") "); } if (mAddress != null && mAddress.length() != 0) { sb.append('<'); sb.append(mAddress); sb.append('>'); } return sb.toString(); } /** * Returns the name, conservatively quoting it if there are any * characters that are likely to cause trouble outside of a * quoted string, or returning it literally if it seems safe. */ public static String quoteNameIfNecessary(String name) { int len = name.length(); for (int i = 0; i < len; i++) { char c = name.charAt(i); if (! ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == ' ') || (c >= '0' && c <= '9'))) { return '"' + quoteName(name) + '"'; } } return name; } /** * Returns the name, with internal backslashes and quotation marks * preceded by backslashes. The outer quote marks themselves are not * added by this method. */ public static String quoteName(String name) { StringBuilder sb = new StringBuilder(); int len = name.length(); for (int i = 0; i < len; i++) { char c = name.charAt(i); if (c == '\\' || c == '"') { sb.append('\\'); } sb.append(c); } return sb.toString(); } /** * Returns the comment, with internal backslashes and parentheses * preceded by backslashes. The outer parentheses themselves are * not added by this method. */ public static String quoteComment(String comment) { int len = comment.length(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { char c = comment.charAt(i); if (c == '(' || c == ')' || c == '\\') { sb.append('\\'); } sb.append(c); } return sb.toString(); } public int hashCode() { int result = 17; if (mName != null) result = 31 * result + mName.hashCode(); if (mAddress != null) result = 31 * result + mAddress.hashCode(); if (mComment != null) result = 31 * result + mComment.hashCode(); return result; } private static boolean stringEquals(String a, String b) { if (a == null) { return (b == null); } else { return (a.equals(b)); } } public boolean equals(@Nullable Object o) { if (!(o instanceof Rfc822Token)) { return false; } Rfc822Token other = (Rfc822Token) o; return (stringEquals(mName, other.mName) && stringEquals(mAddress, other.mAddress) && stringEquals(mComment, other.mComment)); } } mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Tokenizer.java 0 → 100644 +313 −0 Original line number Diff line number Diff line /* * 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.mail.helper; import java.util.ArrayList; import java.util.Collection; /** * This class works as a Tokenizer for MultiAutoCompleteTextView for * address list fields, and also provides a method for converting * a string of addresses (such as might be typed into such a field) * into a series of Rfc822Tokens. */ public class Rfc822Tokenizer { /** * This constructor will try to take a string like * "Foo Bar (something) <foo\@google.com>, * blah\@google.com (something)" * and convert it into one or more Rfc822Tokens, output into the supplied * collection. * * It does *not* decode MIME encoded-words; charset conversion * must already have taken place if necessary. * It will try to be tolerant of broken syntax instead of * returning an error. * */ public static void tokenize(CharSequence text, Collection<Rfc822Token> out) { StringBuilder name = new StringBuilder(); StringBuilder address = new StringBuilder(); StringBuilder comment = new StringBuilder(); int i = 0; int cursor = text.length(); while (i < cursor) { char c = text.charAt(i); if (c == ',' || c == ';') { i++; while (i < cursor && text.charAt(i) == ' ') { i++; } crunch(name); if (address.length() > 0) { out.add(new Rfc822Token(name.toString(), address.toString(), comment.toString())); } else if (name.length() > 0) { out.add(new Rfc822Token(null, name.toString(), comment.toString())); } name.setLength(0); address.setLength(0); comment.setLength(0); } else if (c == '"') { i++; while (i < cursor) { c = text.charAt(i); if (c == '"') { i++; break; } else if (c == '\\') { if (i + 1 < cursor) { name.append(text.charAt(i + 1)); } i += 2; } else { name.append(c); i++; } } } else if (c == '(') { int level = 1; i++; while (i < cursor && level > 0) { c = text.charAt(i); if (c == ')') { if (level > 1) { comment.append(c); } level--; i++; } else if (c == '(') { comment.append(c); level++; i++; } else if (c == '\\') { if (i + 1 < cursor) { comment.append(text.charAt(i + 1)); } i += 2; } else { comment.append(c); i++; } } } else if (c == '<') { i++; while (i < cursor) { c = text.charAt(i); if (c == '>') { i++; break; } else { address.append(c); i++; } } } else if (c == ' ') { name.append('\0'); i++; } else { name.append(c); i++; } } crunch(name); if (address.length() > 0) { out.add(new Rfc822Token(name.toString(), address.toString(), comment.toString())); } else if (name.length() > 0) { out.add(new Rfc822Token(null, name.toString(), comment.toString())); } } /** * This method will try to take a string like * "Foo Bar (something) <foo\@google.com>, * blah\@google.com (something)" * and convert it into one or more Rfc822Tokens. * It does *not* decode MIME encoded-words; charset conversion * must already have taken place if necessary. * It will try to be tolerant of broken syntax instead of * returning an error. */ public static Rfc822Token[] tokenize(CharSequence text) { ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>(); tokenize(text, out); return out.toArray(new Rfc822Token[out.size()]); } private static void crunch(StringBuilder sb) { int i = 0; int len = sb.length(); while (i < len) { char c = sb.charAt(i); if (c == '\0') { if (i == 0 || i == len - 1 || sb.charAt(i - 1) == ' ' || sb.charAt(i - 1) == '\0' || sb.charAt(i + 1) == ' ' || sb.charAt(i + 1) == '\0') { sb.deleteCharAt(i); len--; } else { i++; } } else { i++; } } for (i = 0; i < len; i++) { if (sb.charAt(i) == '\0') { sb.setCharAt(i, ' '); } } } /** * {@inheritDoc} */ public int findTokenStart(CharSequence text, int cursor) { /* * It's hard to search backward, so search forward until * we reach the cursor. */ int best = 0; int i = 0; while (i < cursor) { i = findTokenEnd(text, i); if (i < cursor) { i++; // Skip terminating punctuation while (i < cursor && text.charAt(i) == ' ') { i++; } if (i < cursor) { best = i; } } } return best; } /** * {@inheritDoc} */ public int findTokenEnd(CharSequence text, int cursor) { int len = text.length(); int i = cursor; while (i < len) { char c = text.charAt(i); if (c == ',' || c == ';') { return i; } else if (c == '"') { i++; while (i < len) { c = text.charAt(i); if (c == '"') { i++; break; } else if (c == '\\' && i + 1 < len) { i += 2; } else { i++; } } } else if (c == '(') { int level = 1; i++; while (i < len && level > 0) { c = text.charAt(i); if (c == ')') { level--; i++; } else if (c == '(') { level++; i++; } else if (c == '\\' && i + 1 < len) { i += 2; } else { i++; } } } else if (c == '<') { i++; while (i < len) { c = text.charAt(i); if (c == '>') { i++; break; } else { i++; } } } else { i++; } } return i; } /** * Terminates the specified address with a comma and space. * This assumes that the specified text already has valid syntax. * The Adapter subclass's convertToString() method must make that * guarantee. */ public CharSequence terminateToken(CharSequence text) { return text + ", "; } } mail/common/src/main/java/com/fsck/k9/mail/helper/TextUtils.kt 0 → 100644 +11 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.helper object TextUtils { @JvmStatic fun isEmpty(text: String?) = text.isNullOrEmpty() @JvmStatic fun join(separator: String, items: Array<Any>): String { return items.joinToString(separator) } } Loading
mail/common/src/main/java/com/fsck/k9/mail/Address.java +3 −4 Original line number Diff line number Diff line Loading @@ -7,6 +7,9 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import com.fsck.k9.mail.helper.Rfc822Token; import com.fsck.k9.mail.helper.Rfc822Tokenizer; import com.fsck.k9.mail.helper.TextUtils; import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.codec.DecodeMonitor; import org.apache.james.mime4j.codec.EncoderUtil; Loading @@ -17,10 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.VisibleForTesting; import timber.log.Timber; import android.text.TextUtils; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; public class Address implements Serializable { private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); Loading
mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Token.java 0 → 100644 +205 −0 Original line number Diff line number Diff line /* * 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.mail.helper; import org.jetbrains.annotations.Nullable; /** * This class stores an RFC 822-like name, address, and comment, * and provides methods to convert them to quoted strings. */ public class Rfc822Token { @Nullable private String mName, mAddress, mComment; /** * Creates a new Rfc822Token with the specified name, address, * and comment. */ public Rfc822Token(@Nullable String name, @Nullable String address, @Nullable String comment) { mName = name; mAddress = address; mComment = comment; } /** * Returns the name part. */ @Nullable public String getName() { return mName; } /** * Returns the address part. */ @Nullable public String getAddress() { return mAddress; } /** * Returns the comment part. */ @Nullable public String getComment() { return mComment; } /** * Changes the name to the specified name. */ public void setName(@Nullable String name) { mName = name; } /** * Changes the address to the specified address. */ public void setAddress(@Nullable String address) { mAddress = address; } /** * Changes the comment to the specified comment. */ public void setComment(@Nullable String comment) { mComment = comment; } /** * Returns the name (with quoting added if necessary), * the comment (in parentheses), and the address (in angle brackets). * This should be suitable for inclusion in an RFC 822 address list. */ public String toString() { StringBuilder sb = new StringBuilder(); if (mName != null && mName.length() != 0) { sb.append(quoteNameIfNecessary(mName)); sb.append(' '); } if (mComment != null && mComment.length() != 0) { sb.append('('); sb.append(quoteComment(mComment)); sb.append(") "); } if (mAddress != null && mAddress.length() != 0) { sb.append('<'); sb.append(mAddress); sb.append('>'); } return sb.toString(); } /** * Returns the name, conservatively quoting it if there are any * characters that are likely to cause trouble outside of a * quoted string, or returning it literally if it seems safe. */ public static String quoteNameIfNecessary(String name) { int len = name.length(); for (int i = 0; i < len; i++) { char c = name.charAt(i); if (! ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == ' ') || (c >= '0' && c <= '9'))) { return '"' + quoteName(name) + '"'; } } return name; } /** * Returns the name, with internal backslashes and quotation marks * preceded by backslashes. The outer quote marks themselves are not * added by this method. */ public static String quoteName(String name) { StringBuilder sb = new StringBuilder(); int len = name.length(); for (int i = 0; i < len; i++) { char c = name.charAt(i); if (c == '\\' || c == '"') { sb.append('\\'); } sb.append(c); } return sb.toString(); } /** * Returns the comment, with internal backslashes and parentheses * preceded by backslashes. The outer parentheses themselves are * not added by this method. */ public static String quoteComment(String comment) { int len = comment.length(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { char c = comment.charAt(i); if (c == '(' || c == ')' || c == '\\') { sb.append('\\'); } sb.append(c); } return sb.toString(); } public int hashCode() { int result = 17; if (mName != null) result = 31 * result + mName.hashCode(); if (mAddress != null) result = 31 * result + mAddress.hashCode(); if (mComment != null) result = 31 * result + mComment.hashCode(); return result; } private static boolean stringEquals(String a, String b) { if (a == null) { return (b == null); } else { return (a.equals(b)); } } public boolean equals(@Nullable Object o) { if (!(o instanceof Rfc822Token)) { return false; } Rfc822Token other = (Rfc822Token) o; return (stringEquals(mName, other.mName) && stringEquals(mAddress, other.mAddress) && stringEquals(mComment, other.mComment)); } }
mail/common/src/main/java/com/fsck/k9/mail/helper/Rfc822Tokenizer.java 0 → 100644 +313 −0 Original line number Diff line number Diff line /* * 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.mail.helper; import java.util.ArrayList; import java.util.Collection; /** * This class works as a Tokenizer for MultiAutoCompleteTextView for * address list fields, and also provides a method for converting * a string of addresses (such as might be typed into such a field) * into a series of Rfc822Tokens. */ public class Rfc822Tokenizer { /** * This constructor will try to take a string like * "Foo Bar (something) <foo\@google.com>, * blah\@google.com (something)" * and convert it into one or more Rfc822Tokens, output into the supplied * collection. * * It does *not* decode MIME encoded-words; charset conversion * must already have taken place if necessary. * It will try to be tolerant of broken syntax instead of * returning an error. * */ public static void tokenize(CharSequence text, Collection<Rfc822Token> out) { StringBuilder name = new StringBuilder(); StringBuilder address = new StringBuilder(); StringBuilder comment = new StringBuilder(); int i = 0; int cursor = text.length(); while (i < cursor) { char c = text.charAt(i); if (c == ',' || c == ';') { i++; while (i < cursor && text.charAt(i) == ' ') { i++; } crunch(name); if (address.length() > 0) { out.add(new Rfc822Token(name.toString(), address.toString(), comment.toString())); } else if (name.length() > 0) { out.add(new Rfc822Token(null, name.toString(), comment.toString())); } name.setLength(0); address.setLength(0); comment.setLength(0); } else if (c == '"') { i++; while (i < cursor) { c = text.charAt(i); if (c == '"') { i++; break; } else if (c == '\\') { if (i + 1 < cursor) { name.append(text.charAt(i + 1)); } i += 2; } else { name.append(c); i++; } } } else if (c == '(') { int level = 1; i++; while (i < cursor && level > 0) { c = text.charAt(i); if (c == ')') { if (level > 1) { comment.append(c); } level--; i++; } else if (c == '(') { comment.append(c); level++; i++; } else if (c == '\\') { if (i + 1 < cursor) { comment.append(text.charAt(i + 1)); } i += 2; } else { comment.append(c); i++; } } } else if (c == '<') { i++; while (i < cursor) { c = text.charAt(i); if (c == '>') { i++; break; } else { address.append(c); i++; } } } else if (c == ' ') { name.append('\0'); i++; } else { name.append(c); i++; } } crunch(name); if (address.length() > 0) { out.add(new Rfc822Token(name.toString(), address.toString(), comment.toString())); } else if (name.length() > 0) { out.add(new Rfc822Token(null, name.toString(), comment.toString())); } } /** * This method will try to take a string like * "Foo Bar (something) <foo\@google.com>, * blah\@google.com (something)" * and convert it into one or more Rfc822Tokens. * It does *not* decode MIME encoded-words; charset conversion * must already have taken place if necessary. * It will try to be tolerant of broken syntax instead of * returning an error. */ public static Rfc822Token[] tokenize(CharSequence text) { ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>(); tokenize(text, out); return out.toArray(new Rfc822Token[out.size()]); } private static void crunch(StringBuilder sb) { int i = 0; int len = sb.length(); while (i < len) { char c = sb.charAt(i); if (c == '\0') { if (i == 0 || i == len - 1 || sb.charAt(i - 1) == ' ' || sb.charAt(i - 1) == '\0' || sb.charAt(i + 1) == ' ' || sb.charAt(i + 1) == '\0') { sb.deleteCharAt(i); len--; } else { i++; } } else { i++; } } for (i = 0; i < len; i++) { if (sb.charAt(i) == '\0') { sb.setCharAt(i, ' '); } } } /** * {@inheritDoc} */ public int findTokenStart(CharSequence text, int cursor) { /* * It's hard to search backward, so search forward until * we reach the cursor. */ int best = 0; int i = 0; while (i < cursor) { i = findTokenEnd(text, i); if (i < cursor) { i++; // Skip terminating punctuation while (i < cursor && text.charAt(i) == ' ') { i++; } if (i < cursor) { best = i; } } } return best; } /** * {@inheritDoc} */ public int findTokenEnd(CharSequence text, int cursor) { int len = text.length(); int i = cursor; while (i < len) { char c = text.charAt(i); if (c == ',' || c == ';') { return i; } else if (c == '"') { i++; while (i < len) { c = text.charAt(i); if (c == '"') { i++; break; } else if (c == '\\' && i + 1 < len) { i += 2; } else { i++; } } } else if (c == '(') { int level = 1; i++; while (i < len && level > 0) { c = text.charAt(i); if (c == ')') { level--; i++; } else if (c == '(') { level++; i++; } else if (c == '\\' && i + 1 < len) { i += 2; } else { i++; } } } else if (c == '<') { i++; while (i < len) { c = text.charAt(i); if (c == '>') { i++; break; } else { i++; } } } else { i++; } } return i; } /** * Terminates the specified address with a comma and space. * This assumes that the specified text already has valid syntax. * The Adapter subclass's convertToString() method must make that * guarantee. */ public CharSequence terminateToken(CharSequence text) { return text + ", "; } }
mail/common/src/main/java/com/fsck/k9/mail/helper/TextUtils.kt 0 → 100644 +11 −0 Original line number Diff line number Diff line package com.fsck.k9.mail.helper object TextUtils { @JvmStatic fun isEmpty(text: String?) = text.isNullOrEmpty() @JvmStatic fun join(separator: String, items: Array<Any>): String { return items.joinToString(separator) } }