Java tutorial
/* * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.mail; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; import javax.mail.Address; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import mitm.common.locale.DefaultLocale; import mitm.common.util.MiscStringUtils; import org.apache.commons.lang.CharSet; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Some general purpose utilities for email address validation etc. * * @author Martijn Brinkers * */ public class EmailAddressUtils { private final static Logger logger = LoggerFactory.getLogger(EmailAddressUtils.class); /* * If a local part of an email address only contains these characters, the * local part of the email address does not have to be quoted. */ private static final CharSet NO_QUOTE_LOCALPART = CharSet.getInstance("a-zA-Z0-9_+."); /* * Email address to use when an email address is invalid */ public static final String INVALID_EMAIL = "invalid@invalid.tld"; /** * Email address pattern should not be used for the validation of email addresses because they are not * in line with the one from Javamail. Only use this pattern for creating visual mark-ups etc. */ public static final String EMAIL_REG_EXPR = "[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})"; /** * The pattern used for matching email addresses (non strict matching!). Should be used for matching email * addresses for visual feedback (highlighting of email addresses etc.) */ public static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REG_EXPR); /** * Removes quotes from an email address. Email addresses can be quoted. This * however can be problematic when we need to use the email address as a key. * For example "test"@example.com is in principle the same email address as * test@example.com. This function will strip away any quotes from email * addresses. * * Outlook sometimes will single quote an email address for email adresses * in headers. */ public static String stripQuotes(String email) { if (email == null) { return null; } String unquotedEmail = email.trim(); /* * First check if the whole email address is single quoted * (Outlook sometimes adds single quotes to headers) * example: 'test@example.com' */ unquotedEmail = MiscStringUtils.unquote(unquotedEmail, '\''); unquotedEmail = MiscStringUtils.unquote(unquotedEmail, '"'); /* the local part of an email address can be quoted. */ int atIndex = unquotedEmail.lastIndexOf('@'); /* did the email address contain the @ symbol? */ if (atIndex > -1) { String localPart = unquotedEmail.substring(0, atIndex).trim(); String domainPart = unquotedEmail.substring(atIndex).trim(); localPart = MiscStringUtils.unquote(localPart, '"'); unquotedEmail = localPart + domainPart; } return unquotedEmail; } /** * Checks whether the given email address is a valid email address using strict checks * @param email * @return email address if email is valid, null if email is invalid */ public static String validate(String email) { /* email size must be smaller than max domain + max local */ if (email != null && email.length() < (255 + 65)) { try { InternetAddress validated = new InternetAddress(email, true /* strict checking */); return validated.getAddress(); } catch (AddressException ignored) { logger.debug("Email address \"{}\" is not a valid email address", email); } } return null; } /** * Returns true if the email is a valid email address. */ public static boolean isValid(String email) { return validate(email) != null; } public static boolean isValid(InternetAddress address) { return address != null && validate(address.getAddress()) != null; } /** * Transform the given email address in a standard form so we can * use it as a key using the following steps. When we need to use * an email address as an identifier we need to transform an email * address to a standard form ie. we need to canonicalize the email * address because the same email address can be specified in * different ways. In principle email addresses are case sensitive * however in practice email addresses are almost always not case * sensitive. We therefore will transform the email address to * lowercase. The local part of an email address can be quoted * to support characters that are normally not allowed for the * local part. Examples: "has space"@example.com, "end."@example.com. * These email addresses would, in principle, not be valid * without the quotes. But, "test"@example.com is equal to * test@example.com so we must transform these email addresses * to a standard form. * 1. remove start/end quotes * 2. trim * 3. make all characters lower case * @param email * @return */ public static String canonicalize(String email) { if (email == null) { return null; } String canonical = email.toLowerCase(DefaultLocale.getDefaultLocale()); canonical = stripQuotes(canonical); return canonical; } /** * Canonicalizes and validates the given email address. If nullIfInvalid is true and email address is not a valid email address * null is returned. If nullIfInvalid is false and email is invalid INVALID_EMAIL is returned. */ public static String canonicalizeAndValidate(String email, boolean nullIfInvalid) { String normalized = canonicalize(email); normalized = validate(normalized); if (normalized == null && !nullIfInvalid) { return INVALID_EMAIL; } return normalized; } /** * Canonicalizes and validates the given collection of email addresses and returns a Set * of email addresses (duplicates will therefore be removed) * @return canonicalized and valid emails. Never null. */ public static Set<String> canonicalizeAndValidate(Collection<String> emails, boolean skipIfInvalid) { Set<String> normalizedEmails = new HashSet<String>(); if (emails != null) { for (String email : emails) { String normalized = canonicalizeAndValidate(email, skipIfInvalid); if (normalized != null) { normalizedEmails.add(normalized); } } } return normalizedEmails; } /** * Canonicalizes and validates the given collection of email addresses and returns a Set * of email addresses (duplicates will therefore be removed) * @return canonicalized and valid emails. Never null. */ public static Set<String> canonicalizeAndValidate(String[] emails, boolean skipIfInvalid) { return emails != null ? canonicalizeAndValidate(Arrays.asList(emails), skipIfInvalid) : canonicalizeAndValidate((Collection<String>) null, skipIfInvalid); } /** * Returns the domain part ie. everything after the @. If there is no @ null is returned. The email address is * not normalized or validated. It's up to the caller to validate the email address. */ public static String getDomain(String email) { String domain = StringUtils.substringAfterLast(email, "@"); if (StringUtils.isEmpty(domain)) { domain = null; } return domain; } /** * See: getDomain(email) */ public static String getDomain(InternetAddress email) { if (email == null) { return null; } return getDomain(email.getAddress()); } /** * Returns the local part of an email address ie. everything before the @. If there is no @ the complete string is * returned. The email address is not normalized or validated. It's up to the caller to validate the email address. * If there is no local part null is returned. */ public static String getLocalPart(String email) { String local = StringUtils.substringBeforeLast(email, "@"); if (StringUtils.isEmpty(local)) { local = null; } return local; } /** * See getLocalPart(String email) */ public static String getLocalPart(InternetAddress email) { if (email == null) { return null; } return getLocalPart(email.getAddress()); } /** * If the local part contains any characters which are not allowed in the localpart * and the localpart is not yet quoted, the local part will be quoted. */ public static String quoteLocalPart(String email) { if (email == null) { return null; } String localPart = getLocalPart(email); if (localPart == null) { return email; } if (!MiscStringUtils.isQuoted(localPart, '"')) { boolean shouldQuote = false; for (int i = 0; i < localPart.length(); i++) { char c = localPart.charAt(i); if (NO_QUOTE_LOCALPART.contains(c)) { continue; } shouldQuote = true; break; } if (shouldQuote) { email = "\"" + localPart + "\"@" + getDomain(email); } } return email; } /** * Returns the email part if address is an InternetAddress, if not toString() is called in the Address. */ public static String getEmailAddress(Address address) { String email; if (address == null) { return null; } if (address instanceof InternetAddress) { email = ((InternetAddress) address).getAddress(); } else { email = address.toString(); } return email; } /** * Returns the first address from addresses. Null if addresses is null or addresses.length == 0. */ public static Address getAddress(Address[] addresses) { if (addresses == null || addresses.length == 0) { return null; } return addresses[0]; } /** * Returns the Address's as string array. If decode is true, the address will be mime decoded. * Returns null if addresses is null. Decode is used to decode the user part (if encoded) of an * email address because this is not decoded by default. * * WARNING: The returned string includes the user part! */ public static String[] addressesToStrings(Address[] addresses, boolean decode) { if (addresses == null) { return null; } return addressesToStrings(Arrays.asList(addresses), decode).toArray(new String[] {}); } /** * Returns the Address's as List of strings. If decode is true, the address will be mime decoded. * Returns null if addresses is null. Decode is used to decode the user part (if encoded) of an * email address because this is not decoded by default. * * WARNING: The returned string includes the user part! */ public static ArrayList<String> addressesToStrings(Collection<? extends Address> addresses, boolean decode) { if (addresses == null) { return null; } ArrayList<String> result = new ArrayList<String>(addresses.size()); for (Address address : addresses) { if (address != null) { result.add(decode ? HeaderUtils.decodeTextQuietly(address.toString()) : address.toString()); } } return result; } /** * Returns true of search is contained in emails. The emails are canonicalized and checked * for validity before being compared. */ public static boolean containsEmail(String search, Collection<String> emails) { String filteredSearch = canonicalizeAndValidate(search, true); if (filteredSearch == null || emails == null) { return false; } Set<String> filteredEmails = canonicalizeAndValidate(emails, true); return filteredEmails.contains(filteredSearch); } /** * Returns true if email is equal to INVALID_EMAIL (invalid@invalid.tld) */ public static boolean isInvalidDummyAddress(String email) { return INVALID_EMAIL.equals(email); } /** * Returns true if email is equal to INVALID_EMAIL (invalid@invalid.tld) */ public static boolean isInvalidDummyAddress(InternetAddress email) { if (email == null) { return false; } return INVALID_EMAIL.equals(email.getAddress()); } /** * Returns the Reply-To not throwing any exception. If the Reply-To header cannot be retrieved, null will be * returned. */ public static Address[] getReplyToQuietly(MimeMessage message) { Address[] replyTos = null; if (message != null) { try { replyTos = message.getReplyTo(); } catch (Exception e) { logger.debug("Reply-To is not valid", e); } } return replyTos; } /** * Returns the From not throwing any exception. If the From header cannot be retrieved, null will be * returned. */ public static Address[] getFromQuietly(MimeMessage message) { Address[] froms = null; if (message != null) { try { froms = message.getFrom(); } catch (Exception e) { logger.debug("From is not valid", e); } } return froms; } }