001package votorola.g.mail; // Copyright 2008-2009, Michael Allan.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
002
003import java.io.*;
004import java.util.regex.Pattern;
005import javax.mail.internet.*;
006import votorola.g.lang.*;
007
008
009/** Internet (email) address utilities.
010  *
011  *     @see <a href='http://tools.ietf.org/html/rfc822'
012  *                               >ietf.org/html/rfc822</a>
013  */
014public final @ThreadSafe class InternetAddressX
015{
016
017    private InternetAddressX() {}
018
019
020
021    /** Converts an email address to canonical form.  The canonical form is the bare
022      * addr-spec having no personal part and no angle braces, and with the domain name in
023      * lower case.  This method does strict parsing.
024      *
025      *     @see #canonicalize(InternetAddress)
026      *     @see #VALID_PATTERN_BARE_DOMNAME
027      */
028    public static String canonicalAddress( final String address ) throws AddressException
029    {
030        final InternetAddress iAddress = new InternetAddress( address, /*strict*/true );
031     // iAddress.validate(); // probably redundant with strict parsing in constructor
032        if( !VALID_PATTERN_BARE_DOMNAME.matcher( iAddress.getAddress() ).matches() ) // stricter checks
033        {
034            throw new AddressException( "malformed email address: \"" + iAddress.getAddress()
035              + "\"" );
036        }
037
038        canonicalize( iAddress );
039        return iAddress.getAddress();
040    }
041
042
043
044    /** Converts an email address to canonical form.
045      *
046      *     @see #canonicalAddress(String)
047      */
048    public static void canonicalize( final InternetAddress iAddress )
049    {
050        final String addrSpec = iAddress.getAddress();
051        final int a = addrSpec.indexOf( '@' );
052        iAddress.setAddress( addrSpec.substring(0,a) + "@"
053          + addrSpec.substring(a+1).toLowerCase() );
054    }
055
056
057
058    /** Returns the local-part of the address.  This is the part prior to the '@'
059      * character in the addr-spec.  It is typically a user name.
060      *
061      *     @param iAddress a single (non-group) address
062      *
063      *     @throws AddressException if the address is a group address,
064      *       or contains no local-part
065      */
066    public static String localPart( final InternetAddress iAddress ) throws AddressException
067    {
068        if( iAddress.isGroup() ) throw new AddressException( "local-part requested of group address: " + iAddress );
069
070        return localPart( iAddress.getAddress() );
071    }
072
073
074
075    /** Returns the local-part of the address.  This is the part prior to the '@'
076      * character.  It is typically a user name.
077      *
078      *     @param addrSpec the bare addr-spec of an email address, having no
079      *       personal part and no angle braces
080      *
081      *     @throws AddressException if addrSpec contains no local-part
082      */
083    public static String localPart( final String addrSpec ) throws AddressException
084    {
085        final int a = addrSpec.indexOf( '@' );
086        if( a <= 0 ) throw new AddressException( "no local-part in addr-spec: " + addrSpec );
087
088        return addrSpec.substring( 0, a );
089    }
090
091
092
093    /** Constructs an email address from a local-part and a domain name, and validates it.
094      *
095      *     @param loc the local-part
096      *     @param dom the domain name
097      *
098      *     @throws AddressException if loc or dom is null, or if the resulting address
099      *       cannot be validated
100      *
101      *     @see #VALID_PATTERN_BARE_DOMNAME
102      */
103    public static String newValidAddress( final String loc, final String dom )
104      throws AddressException
105    {
106        if( loc == null || loc.length() == 0 )
107        {
108            throw new AddressException( "malformed email address, no local-part" );
109        }
110
111        if( dom == null || dom.length() == 0 ) 
112        {
113            throw new AddressException( "malformed email address, no domain name" );
114        }
115
116        final String address = loc + "@" + dom;
117        if( !VALID_PATTERN_BARE_DOMNAME.matcher( address ).matches() )
118        {
119            throw new AddressException( "malformed email address: \"" + address + "\"" );
120        }
121
122        return address;
123    }
124
125
126
127    /** Same as {@linkplain InternetAddress#setPersonal(String) setPersonal}(name),
128      * but returns any UnsupportedEncodingException that occurs, rather than throwing it.
129      *
130      *     @return any UnsupportedEncodingException that occured
131      */
132    public static UnsupportedEncodingException trySetPersonal(
133      InternetAddress iAddress, String name )
134    {
135        try
136        {
137            iAddress.setPersonal( name );
138            return null;
139        }
140        catch( UnsupportedEncodingException x ) { return x; }
141    }
142
143
144
145    /** Same as {@linkplain InternetAddress#setPersonal(String,String) setPersonal}(name,charset),
146      * but returns any UnsupportedEncodingException that occurs, rather than throwing it.
147      *
148      *     @return any UnsupportedEncodingException that occured
149      */
150    public static UnsupportedEncodingException trySetPersonal(
151      InternetAddress iAddress, String name, String charset )
152    {
153        try
154        {
155            iAddress.setPersonal( name, charset );
156            return null;
157        }
158        catch( UnsupportedEncodingException x ) { return x; }
159    }
160
161
162
163    /** The pattern of valid email address consisting of the bare addr-spec having no
164      * personal part and no angle braces, and with no domain literals (like
165      * <code>joe@[192.168.1.100]</code>).
166      *
167      *     @see AddressValidationP
168      */
169    public static final Pattern VALID_PATTERN_BARE_DOMNAME = AddressValidationP.newPattern();
170
171
172
173}