001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.net.mail; 020 021import java.io.UnsupportedEncodingException; 022import java.net.IDN; 023import java.util.ArrayList; 024import java.util.Iterator; 025 026import javax.mail.internet.AddressException; 027import javax.mail.internet.InternetAddress; 028import javax.mail.internet.MimeUtility; 029 030import lucee.commons.lang.StringUtil; 031import lucee.runtime.exp.PageException; 032import lucee.runtime.op.Caster; 033import lucee.runtime.op.Decision; 034import lucee.runtime.type.Array; 035import lucee.runtime.type.Struct; 036import lucee.runtime.type.util.ListUtil; 037 038public final class MailUtil { 039 040 public static String encode(String text,String encoding) throws UnsupportedEncodingException { 041 //print.ln(StringUtil.changeCharset(text,encoding)); 042 return MimeUtility.encodeText(text,encoding,"Q"); 043 } 044 045 public static String decode(String text) throws UnsupportedEncodingException { 046 return MimeUtility.decodeText(text); 047 } 048 049 050 051 public static InternetAddress toInternetAddress(Object emails) throws MailException, UnsupportedEncodingException, PageException { 052 053 if ( emails instanceof String ) 054 return parseEmail( emails ); 055 056 InternetAddress[] addresses = toInternetAddresses( emails ); 057 if ( addresses != null && addresses.length > 0 ) 058 return addresses[0]; 059 060 return null; 061 } 062 063 064 public static InternetAddress[] toInternetAddresses(Object emails) throws MailException, UnsupportedEncodingException, PageException { 065 066 if (emails instanceof String ) 067 return fromList((String) emails); 068 069 else if ( Decision.isArray(emails) ) 070 return fromArray(Caster.toArray(emails)); 071 072 else if ( Decision.isStruct(emails) ) 073 return new InternetAddress[]{ fromStruct(Caster.toStruct(emails)) }; 074 075 else 076 throw new MailException("e-mail defintions must be one of the following types [string,array,struct], not ["+emails.getClass().getName()+"]"); 077 } 078 079 080 private static InternetAddress[] fromArray(Array array) throws MailException, PageException, UnsupportedEncodingException { 081 082 Iterator it = array.valueIterator(); 083 Object el; 084 ArrayList<InternetAddress> pairs = new ArrayList(); 085 086 while(it.hasNext()){ 087 el=it.next(); 088 if ( Decision.isStruct( el ) ) { 089 090 pairs.add( fromStruct(Caster.toStruct(el)) ); 091 } 092 else { 093 094 InternetAddress addr = parseEmail( Caster.toString(el) ); 095 if ( addr != null ) 096 pairs.add( addr ); 097 } 098 } 099 100 return pairs.toArray( new InternetAddress[ pairs.size() ] ); 101 } 102 103 104 private static InternetAddress fromStruct( Struct sct ) throws MailException, UnsupportedEncodingException { 105 106 String name = Caster.toString(sct.get("label",null),null); 107 if ( name == null ) 108 name=Caster.toString(sct.get("name",null),null); 109 110 String email = Caster.toString(sct.get("email",null),null); 111 if ( email == null ) 112 email = Caster.toString(sct.get("e-mail",null),null); 113 if ( email == null ) 114 email = Caster.toString(sct.get("mail",null),null); 115 116 if( StringUtil.isEmpty(email) ) 117 throw new MailException("missing e-mail definition in struct"); 118 119 if(name==null) name=""; 120 121 return new InternetAddress( email, name ); 122 } 123 124 125 private static InternetAddress[] fromList( String strEmails ) { 126 127 if ( StringUtil.isEmpty( strEmails, true ) ) 128 return new InternetAddress[0]; 129 130 Array raw = ListUtil.listWithQuotesToArray(strEmails, ",;", "\""); 131 132 Iterator<Object> it = raw.valueIterator(); 133 ArrayList<InternetAddress> al = new ArrayList(); 134 135 while( it.hasNext() ) { 136 137 InternetAddress addr = parseEmail( it.next() ); 138 139 if( addr != null ) 140 al.add( addr ); 141 } 142 143 return al.toArray( new InternetAddress[ al.size() ] ); 144 } 145 146 147 /** 148 * returns true if the passed value is a in valid email address format 149 * @param value 150 * @return 151 */ 152 public static boolean isValidEmail( Object value ) { 153 154 InternetAddress addr = parseEmail( value ); 155 156 if ( addr != null ) { 157 158 String address = addr.getAddress(); 159 160 if ( address.contains( ".." ) ) 161 return false; 162 163 int pos = address.indexOf( '@' ); 164 165 if ( pos < 1 || pos == address.length() - 1 ) 166 return false; 167 168 String local = address.substring(0, pos); 169 String domain = address.substring(pos + 1); 170 171 if ( domain.charAt( 0 ) == '.' 172 || local.charAt( 0 ) == '.' 173 || local.charAt( local.length() - 1 ) == '.' ) 174 return false; 175 176 pos = domain.lastIndexOf( '.' ); 177 178 if ( pos > 0 && pos < domain.length() - 2 ) // test TLD to be at least 2 chars all alpha characters 179 return StringUtil.isAllAlpha( domain.substring( pos + 1 ) ); 180 } 181 182 return false; 183 } 184 185 186 /** 187 * returns an InternetAddress object or null if the parsing fails. to be be used in multiple places. 188 * @param value 189 * @return 190 */ 191 public static InternetAddress parseEmail( Object value ) { 192 193 String str = Caster.toString( value, "" ); 194 195 if ( str.indexOf( '@' ) > -1 ) { 196 197 try { 198 199 InternetAddress addr = new InternetAddress( str ); 200 201 fixIDN( addr ); 202 return addr; 203 } 204 catch ( AddressException ex ) {} 205 } 206 207 return null; 208 } 209 210 211 /** 212 * converts IDN to ASCII if needed 213 * @param addr 214 */ 215 public static void fixIDN( InternetAddress addr ) { 216 217 String address = addr.getAddress(); 218 int pos = address.indexOf( '@' ); 219 220 if ( pos > 0 && pos < address.length() - 1 ) { 221 222 String domain = address.substring( pos + 1 ); 223 224 if ( !StringUtil.isAscii( domain ) ) { 225 226 domain = IDN.toASCII( domain ); 227 addr.setAddress( address.substring( 0, pos ) + "@" + domain ); 228 } 229 } 230 } 231 232}