001    package railo.runtime.net.smtp;
002    
003    import java.io.FileNotFoundException;
004    import java.io.Serializable;
005    import java.io.UnsupportedEncodingException;
006    import java.net.URL;
007    import java.text.SimpleDateFormat;
008    import java.util.ArrayList;
009    import java.util.Collections;
010    import java.util.Date;
011    import java.util.HashMap;
012    import java.util.Enumeration;
013    import java.util.Iterator;
014    import java.util.Locale;
015    import java.util.Map;
016    import java.util.Map.Entry;
017    import java.util.Properties;
018    import java.util.TimeZone;
019    
020    import javax.activation.DataHandler;
021    import javax.mail.Authenticator;
022    import javax.mail.BodyPart;
023    import javax.mail.Message;
024    import javax.mail.MessagingException;
025    import javax.mail.Multipart;
026    import javax.mail.internet.AddressException;
027    import javax.mail.internet.InternetAddress;
028    import javax.mail.internet.MimeBodyPart;
029    import javax.mail.internet.MimeMessage;
030    import javax.mail.internet.MimeMultipart;
031    import javax.mail.internet.MimePart;
032    
033    import org.apache.commons.collections.ReferenceMap;
034    
035    import railo.commons.activation.ResourceDataSource;
036    import railo.commons.digest.MD5;
037    import railo.commons.io.SystemUtil;
038    import railo.commons.io.log.LogAndSource;
039    import railo.commons.io.log.LogUtil;
040    import railo.commons.io.res.Resource;
041    import railo.commons.io.res.util.ResourceUtil;
042    import railo.commons.lang.SerializableObject;
043    import railo.commons.lang.StringUtil;
044    import railo.runtime.config.Config;
045    import railo.runtime.config.ConfigWeb;
046    import railo.runtime.config.ConfigWebImpl;
047    import railo.runtime.engine.ThreadLocalPageContext;
048    import railo.runtime.exp.ExpressionException;
049    import railo.runtime.exp.PageException;
050    import railo.runtime.net.mail.MailException;
051    import railo.runtime.net.mail.MailPart;
052    import railo.runtime.net.mail.MailUtil;
053    import railo.runtime.net.mail.Server;
054    import railo.runtime.net.mail.ServerImpl;
055    import railo.runtime.net.proxy.Proxy;
056    import railo.runtime.net.proxy.ProxyData;
057    import railo.runtime.net.proxy.ProxyDataImpl;
058    import railo.runtime.net.smtp.SMTPConnectionPool.SessionAndTransport;
059    import railo.runtime.op.Caster;
060    import railo.runtime.spooler.mail.MailSpoolerTask;
061    import railo.runtime.type.util.ArrayUtil;
062    import railo.runtime.type.util.ListUtil;
063    
064    import com.sun.mail.smtp.SMTPMessage;
065    
066    public final class SMTPClient implements Serializable  {
067    
068            
069    
070            /**
071             * 
072             */
073            private static final long serialVersionUID = 5227282806519740328L;
074            
075            private static final int SPOOL_UNDEFINED=0;
076            private static final int SPOOL_YES=1;
077            private static final int SPOOL_NO=2;
078    
079            private static final int SSL_NONE=0;
080            private static final int SSL_YES=1;
081            private static final int SSL_NO=2;
082    
083            private static final int TLS_NONE=0;
084            private static final int TLS_YES=1;
085            private static final int TLS_NO=2;
086            
087            private static final String TEXT_HTML = "text/html";
088            private static final String TEXT_PLAIN = "text/plain";
089            private static final SerializableObject LOCK = new SerializableObject();
090    
091            private static Map<TimeZone, SimpleDateFormat> formatters=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
092            //private static final int PORT = 25; 
093            
094            private int spool=SPOOL_UNDEFINED;
095            
096            private int timeout=-1;
097            
098            private String plainText;
099            private String plainTextCharset;
100            
101            private String htmlText;
102            
103    
104            private String htmlTextCharset;
105    
106            private Attachment[] attachmentz;
107    
108            private String[] host;
109            private String charset="UTF-8";
110            private InternetAddress from;
111            private InternetAddress[] tos;
112            private InternetAddress[] bccs;
113            private InternetAddress[] ccs;
114            private InternetAddress[] rts;
115            private InternetAddress[] fts;
116            private String subject="";
117            private String xmailer="Railo Mail";
118            private Map<String,String> headers=new HashMap<String,String>();
119            private int port=-1;
120    
121            private String username;
122            private String password="";
123    
124    
125            
126            
127            private int ssl=SSL_NONE;
128            private int tls=TLS_NONE;
129            
130            ProxyData proxyData=new ProxyDataImpl();
131            private ArrayList<MailPart> parts;
132    
133            private TimeZone timeZone;
134            
135            
136            public static String getNow(TimeZone tz){
137                    tz = ThreadLocalPageContext.getTimeZone(tz);
138                    SimpleDateFormat df=formatters.get(tz);
139                    if(df==null) {
140                            df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z (z)",Locale.US);
141                            df.setTimeZone(tz);
142                            formatters.put(tz, df);
143                    }
144                    return df.format(new Date());
145            }
146            
147            
148            public void setSpoolenable(boolean spoolenable) {
149                    spool=spoolenable?SPOOL_YES:SPOOL_NO;
150            }
151    
152            /**
153             * set port of the mailserver
154             * @param port
155             */
156            public void setPort(int port) {
157                    this.port=port;
158            }
159    
160            /**
161             * @param charset the charset to set
162             */
163            public void setCharset(String charset) {
164                    this.charset = charset;
165            }
166    
167            
168            public static ServerImpl toServerImpl(String server,int port, String usr,String pwd) throws MailException {
169                    int index;
170                    
171                    // username/password
172                    index=server.indexOf('@');
173                    if(index!=-1) {
174                            usr=server.substring(0,index);
175                            server=server.substring(index+1);
176                            index=usr.indexOf(':');
177                            if(index!=-1) {
178                                    pwd=usr.substring(index+1);
179                                    usr=usr.substring(0,index);
180                            }
181                    }
182                    
183                    // port
184                    index=server.indexOf(':');
185                    if(index!=-1) {
186                            try {
187                                    port=Caster.toIntValue(server.substring(index+1));
188                            } catch (ExpressionException e) {
189                                    throw new MailException(e.getMessage());
190                            }
191                            server=server.substring(0,index);
192                    }
193                    
194                    
195                    ServerImpl srv = ServerImpl.getInstance(server, port, usr, pwd, false, false);
196                    return srv;
197            }
198            
199            public void setHost(String host) throws PageException {
200                    if(!StringUtil.isEmpty(host,true))this.host = ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(host, ','));
201            } 
202    
203            /**
204             * @param password the password to set
205             */
206            public void setPassword(String password) {
207                    this.password = password;
208            }
209    
210            /**
211             * @param username the username to set
212             */
213            public void setUsername(String username) {
214                    this.username = username;
215            }
216            
217    
218            public void addHeader(String name, String value) {
219                    headers.put(name, value);
220            }
221    
222            public void addTo(InternetAddress to) {
223                    tos=add(tos,to);
224            }
225    
226            public void addTo(Object to) throws UnsupportedEncodingException, PageException, MailException {
227                    InternetAddress[] tmp = MailUtil.toInternetAddresses(to);
228                    for(int i=0;i<tmp.length;i++) {
229                            addTo(tmp[i]);
230                    }
231            }
232    
233            public void setFrom(InternetAddress from) {
234                    this.from=from;
235            }
236    
237            public void setFrom(Object from) throws UnsupportedEncodingException, MailException, PageException {
238                    InternetAddress[] addrs = MailUtil.toInternetAddresses(from);
239                    if(addrs.length==0) return;
240                    setFrom(addrs[0]);
241            }
242            
243            public void addBCC(InternetAddress bcc) {
244                    bccs=add(bccs,bcc);
245            }
246    
247            public void addBCC(Object bcc) throws UnsupportedEncodingException, MailException, PageException {
248                    InternetAddress[] tmp = MailUtil.toInternetAddresses(bcc);
249                    for(int i=0;i<tmp.length;i++) {
250                            addBCC(tmp[i]);
251                    }
252            }
253    
254            public void addCC(InternetAddress cc) {
255                    ccs=add(ccs,cc);
256            }
257    
258            public void addCC(Object cc) throws UnsupportedEncodingException, MailException, PageException {
259                    InternetAddress[] tmp = MailUtil.toInternetAddresses(cc);
260                    for(int i=0;i<tmp.length;i++) {
261                            addCC(tmp[i]);
262                    }
263            }
264            
265            public void addReplyTo(InternetAddress rt) {
266                    rts=add(rts,rt);
267            }
268    
269            public void addReplyTo(Object rt) throws UnsupportedEncodingException, MailException, PageException {
270                    InternetAddress[] tmp = MailUtil.toInternetAddresses(rt);
271                    for(int i=0;i<tmp.length;i++) {
272                            addReplyTo(tmp[i]);
273                    }
274            }
275            
276            public void addFailTo(InternetAddress ft) {
277                    fts=add(fts,ft);
278            }
279    
280            public String getHTMLTextAsString() {
281                    return htmlText;
282            }
283            public String getPlainTextAsString() {
284                    return plainText;
285            }
286    
287            public void addFailTo(Object ft) throws UnsupportedEncodingException, MailException, PageException {
288                    InternetAddress[] tmp = MailUtil.toInternetAddresses(ft);
289                    for(int i=0;i<tmp.length;i++) {
290                            addFailTo(tmp[i]);
291                    }
292            }
293    
294            /**
295             * @param timeout the timeout to set
296             */
297            public void setTimeout(int timeout) {
298                    this.timeout = timeout;
299            }
300            
301            public void setSubject(String subject) {
302                    this.subject=subject;
303            }
304            
305            public void setXMailer(String xmailer) {
306                    this.xmailer=xmailer;
307            }
308    
309            /**
310             * creates a new expanded array and return it;
311             * @param oldArr
312             * @param newValue
313             * @return new expanded array
314             */
315            protected static InternetAddress[] add(InternetAddress[] oldArr, InternetAddress newValue) {
316                    if(oldArr==null) return new InternetAddress[] {newValue};
317                    //else {
318                    InternetAddress[] tmp=new InternetAddress[oldArr.length+1];
319                    for(int i=0;i<oldArr.length;i++) {
320                            tmp[i]=oldArr[i];
321                    }
322                    tmp[oldArr.length]=newValue;    
323                    return tmp;
324                    //}
325            }
326            
327            protected static Attachment[] add(Attachment[] oldArr, Attachment newValue) {
328                    if(oldArr==null) return new Attachment[] {newValue};
329                    //else {
330                    Attachment[] tmp=new Attachment[oldArr.length+1];
331                            for(int i=0;i<oldArr.length;i++) {
332                                    tmp[i]=oldArr[i];
333                            }
334                            tmp[oldArr.length]=newValue;    
335                            return tmp;
336                    //}
337            }
338    
339            public static class MimeMessageAndSession {
340                    public final MimeMessage message;
341                    public final SessionAndTransport session;
342    
343                    public MimeMessageAndSession(MimeMessage message,SessionAndTransport session){
344                            this.message=message;
345                            this.session=session;
346                    }
347            }
348            
349            
350            private MimeMessageAndSession createMimeMessage(railo.runtime.config.Config config,String hostName, int port, String username, String password,
351                            boolean tls,boolean ssl) throws MessagingException {
352                    
353                  Properties props = (Properties) System.getProperties().clone();
354                  
355                  props.put("mail.smtp.host", hostName);
356                  props.put("mail.smtp.timeout", Caster.toString(timeout));
357                  props.put("mail.smtp.connectiontimeout", Caster.toString(timeout));
358                  if(port>0){
359                      props.put("mail.smtp.port", Caster.toString(port));
360                  }
361                  if(ssl)   {
362                  props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
363                      props.put("mail.smtp.socketFactory.port", Caster.toString(port));
364                  props.put("mail.smtp.socketFactory.fallback", "false");
365              }
366              else {
367                      props.put("mail.smtp.socketFactory.class", "javax.net.SocketFactory");
368                      props.remove("mail.smtp.socketFactory.port");
369                  props.remove("mail.smtp.socketFactory.fallback");
370              }
371                  Authenticator auth=null;
372                  if(!StringUtil.isEmpty(username)) {
373                      props.put("mail.smtp.auth", "true"); 
374                      props.put("mail.smtp.starttls.enable",tls?"true":"false");
375                      
376                      props.put("mail.smtp.user", username);
377                      props.put("mail.smtp.password", password);
378                      props.put("password", password);
379                      auth=new SMTPAuthenticator( username, password );
380                  }
381                  else {
382                      props.put("mail.smtp.auth", "false"); 
383                      props.remove("mail.smtp.starttls.enable");
384                      
385                      props.remove("mail.smtp.user");
386                      props.remove("mail.smtp.password");
387                      props.remove("password");
388                  }
389                  SessionAndTransport sat = SMTPConnectionPool.getSessionAndTransport(props,hash(props),auth);
390                  
391            // Contacts
392                    SMTPMessage msg = new SMTPMessage(sat.session);
393                    if(from==null)throw new MessagingException("you have do define the from for the mail"); 
394                    //if(tos==null)throw new MessagingException("you have do define the to for the mail"); 
395                    
396                    checkAddress(from,charset);
397                    //checkAddress(tos,charset);
398                    
399                    msg.setFrom(from);
400                    //msg.setRecipients(Message.RecipientType.TO, tos);
401                
402                    if(tos!=null){
403                            checkAddress(tos,charset);
404                            msg.setRecipients(Message.RecipientType.TO, tos);
405                }
406                    if(ccs!=null){
407                            checkAddress(ccs,charset);
408                    msg.setRecipients(Message.RecipientType.CC, ccs);
409                }
410                if(bccs!=null){
411                            checkAddress(bccs,charset);
412                    msg.setRecipients(Message.RecipientType.BCC, bccs);
413                }
414                if(rts!=null){
415                            checkAddress(rts,charset);
416                    msg.setReplyTo(rts);
417                }
418                if(fts!=null){
419                            checkAddress(fts,charset);
420                    msg.setEnvelopeFrom(fts[0].toString());
421                }
422                
423            // Subject and headers
424                try {
425                            msg.setSubject(MailUtil.encode(subject, charset));
426                    } catch (UnsupportedEncodingException e) {
427                            throw new MessagingException("the encoding "+charset+" is not supported");
428                    }
429                    msg.setHeader("X-Mailer", xmailer);
430                    
431                    msg.setHeader("Date",getNow(timeZone)); 
432                //msg.setSentDate(new Date());
433                            
434                Multipart mp=null;
435                
436                    // Only HTML
437                    if(plainText==null) {
438                            if(ArrayUtil.isEmpty(attachmentz) && ArrayUtil.isEmpty(parts)){
439                                    fillHTMLText(msg);
440                                    setHeaders(msg,headers);
441                                    return new MimeMessageAndSession(msg,sat);
442                            }
443                            mp = new MimeMultipart();
444                            mp.addBodyPart(getHTMLText());
445                    }
446                    // only Plain
447                    else if(htmlText==null) {
448                            if(ArrayUtil.isEmpty(attachmentz) && ArrayUtil.isEmpty(parts)){
449                                    fillPlainText(msg);
450                                    setHeaders(msg,headers);
451                                    return new MimeMessageAndSession(msg,sat);
452                            }
453                            mp = new MimeMultipart();
454                            mp.addBodyPart(getPlainText());
455                    }
456                    // Plain and HTML
457                    else {
458                            mp=new MimeMultipart("alternative");
459                            mp.addBodyPart(getPlainText());
460                            mp.addBodyPart(getHTMLText());
461                            
462                            if(!ArrayUtil.isEmpty(attachmentz) || !ArrayUtil.isEmpty(parts)){
463                                    MimeBodyPart content = new MimeBodyPart();
464                                    content.setContent(mp);
465                                    mp = new MimeMultipart();
466                                    mp.addBodyPart(content);
467                            }
468                    }
469                    
470                    // parts
471                    if(!ArrayUtil.isEmpty(parts)){
472                            Iterator<MailPart> it = parts.iterator();
473                            if(mp instanceof MimeMultipart)
474                                    ((MimeMultipart)mp).setSubType("alternative");
475                            while(it.hasNext()){
476                                    mp.addBodyPart(toMimeBodyPart(it.next()));      
477                            }
478                    }
479                    
480                    // Attachments
481                    if(!ArrayUtil.isEmpty(attachmentz)){
482                            for(int i=0;i<attachmentz.length;i++) {
483                                    mp.addBodyPart(toMimeBodyPart(mp,config,attachmentz[i]));       
484                            }       
485                    }               
486                    msg.setContent(mp);
487                    setHeaders(msg,headers);
488                
489                    return new MimeMessageAndSession(msg,sat);
490            }
491    
492            private static String hash(Properties props) {
493                    Enumeration<?> e = props.propertyNames();
494                    java.util.List<String> names=new ArrayList<String>();
495                    String str;
496                    while(e.hasMoreElements()){
497                            str=Caster.toString(e.nextElement(),null);
498                            if(!StringUtil.isEmpty(str) && str.startsWith("mail.smtp."))
499                                    names.add(str);
500                            
501                    }
502                    Collections.sort(names);
503                    StringBuilder sb=new StringBuilder();
504                    Iterator<String> it = names.iterator();
505                    while(it.hasNext()){
506                            str=it.next();
507                            sb.append(str).append(':').append(props.getProperty(str)).append(';');
508                    }
509                    str=sb.toString();
510                    return MD5.getDigestAsString(str,str);
511                    
512            }
513    
514            private static void setHeaders(SMTPMessage msg, Map<String,String> headers) throws MessagingException {
515                    Iterator<Entry<String, String>> it = headers.entrySet().iterator();
516                    Entry<String, String> e;
517                while(it.hasNext()) {
518                    e = it.next();
519                    msg.setHeader(e.getKey(),e.getValue());
520                }
521            }
522    
523            private void checkAddress(InternetAddress[] ias,String charset) { // DIFF 23
524                    for(int i=0;i<ias.length;i++) {
525                            checkAddress(ias[i], charset);
526                    }
527            }
528            private void checkAddress(InternetAddress ia,String charset) { // DIFF 23
529                    try {
530                            if(!StringUtil.isEmpty(ia.getPersonal())) {
531                                    String personal = MailUtil.encode(ia.getPersonal(), charset);
532                                    if(!personal.equals(ia.getPersonal()))
533                                            ia.setPersonal(personal);
534                            }
535                    } catch (UnsupportedEncodingException e) {}
536            }
537    
538            /**
539             * @param plainText
540             */
541            public void setPlainText(String plainText) {
542                    this.plainText=plainText;
543                    this.plainTextCharset=charset;
544            }
545            
546            /**
547             * @param plainText
548             * @param plainTextCharset
549             */
550            public void setPlainText(String plainText, String plainTextCharset) {
551                    this.plainText=plainText;
552                    this.plainTextCharset=plainTextCharset;
553            }
554            
555            /**
556             * @param htmlText
557             */
558            public void setHTMLText(String htmlText) {
559                    this.htmlText=htmlText;
560                    this.htmlTextCharset=charset;
561            }
562    
563    
564    
565            public boolean hasHTMLText() {
566                    return htmlText!=null;
567            }
568    
569            public boolean hasPlainText() {
570                    return plainText!=null;
571            }
572            
573            /**
574             * @param htmlText
575             * @param htmlTextCharset
576             */
577            public void setHTMLText(String htmlText, String htmlTextCharset) {
578                    this.htmlText=htmlText;
579                    this.htmlTextCharset=htmlTextCharset;
580            }
581            
582            public void addAttachment(URL url) {
583                    Attachment mbp = new Attachment(url);
584                    attachmentz=add(attachmentz, mbp);
585            }
586    
587            public void addAttachment(Resource resource, String type, String disposition, String contentID,boolean removeAfterSend) {
588                    Attachment att = new Attachment(resource, type, disposition, contentID,removeAfterSend);
589                    attachmentz=add(attachmentz, att);
590            }
591            
592            public MimeBodyPart toMimeBodyPart(Multipart mp, railo.runtime.config.Config config,Attachment att) throws MessagingException  {
593                    
594                    MimeBodyPart mbp = new MimeBodyPart();
595                    
596                    // set Data Source
597                    String strRes = att.getAbsolutePath();
598                    if(!StringUtil.isEmpty(strRes)){
599                            
600                            mbp.setDataHandler(new DataHandler(new ResourceDataSource(config.getResource(strRes))));
601                    }
602                    else mbp.setDataHandler(new DataHandler(new URLDataSource2(att.getURL())));
603                    
604                    mbp.setFileName(att.getFileName());
605                    if(!StringUtil.isEmpty(att.getType())) mbp.setHeader("Content-Type", att.getType());
606                    if(!StringUtil.isEmpty(att.getDisposition())){
607                            mbp.setDisposition(att.getDisposition());
608                            if(mp instanceof MimeMultipart)
609                                    ((MimeMultipart)mp).setSubType("related");
610                            
611                    }
612                    if(!StringUtil.isEmpty(att.getContentID()))mbp.setContentID(att.getContentID());
613                            
614                    return mbp;
615            }
616            
617            /**
618             * @param file
619             * @throws MessagingException
620             * @throws FileNotFoundException 
621             */
622            public void addAttachment(Resource file) throws MessagingException {
623                    addAttachment(file,null,null,null,false);
624            }
625            
626    
627            
628            
629            public void send(ConfigWeb config) throws MailException {
630                    if(ArrayUtil.isEmpty(config.getMailServers()) && ArrayUtil.isEmpty(host))
631                            throw new MailException("no SMTP Server defined");
632                    
633                    if(plainText==null && htmlText==null)
634                            throw new MailException("you must define plaintext or htmltext");
635                    
636                    if(timeout<1)timeout=config.getMailTimeout()*1000;
637                    
638                    if(spool==SPOOL_YES || (spool==SPOOL_UNDEFINED && config.isMailSpoolEnable())) {
639                    config.getSpoolerEngine().add(new MailSpoolerTask(this));
640            }
641                    else
642                            _send(config);
643            }
644            
645    
646            public void _send(railo.runtime.config.ConfigWeb config) throws MailException {
647                    long start=System.nanoTime();
648                    try {
649    
650                    Proxy.start(proxyData);
651                    LogAndSource log = config.getMailLogger();
652                    // Server
653            Server[] servers = config.getMailServers();
654            if(host!=null) {
655                    int prt;
656                    String usr,pwd;
657                    ServerImpl[] nServers = new ServerImpl[host.length];
658                    for(int i=0;i<host.length;i++) {
659                            usr=null;pwd=null;
660                            prt=ServerImpl.DEFAULT_PORT;
661                            
662                            if(port>0)prt=port;
663                            if(!StringUtil.isEmpty(username))       {
664                                    usr=username;
665                                    pwd=password;
666                            }
667                            
668                            nServers[i]=toServerImpl(host[i],prt,usr,pwd);
669                                    if(ssl==SSL_YES) nServers[i].setSSL(true);
670                            if(tls==TLS_YES) nServers[i].setTLS(true);
671                                    
672                    }
673                    servers=nServers;
674            }
675                    if(servers.length==0) {
676                            //return;
677                            throw new MailException("no SMTP Server defined");
678                    }
679                    
680                    boolean _ssl,_tls;
681                    for(int i=0;i<servers.length;i++) {
682    
683                            Server server = servers[i];
684                            String _username=null,_password="";
685                            //int _port;
686                            
687                            // username/password
688                            
689                            if(server.hasAuthentication()) {
690                                    _username=server.getUsername();
691                                    _password=server.getPassword();
692                            }
693                            
694                            
695                            // tls
696                            if(tls!=TLS_NONE)_tls=tls==TLS_YES;
697                            else _tls=((ServerImpl)server).isTLS();
698            
699                            // ssl
700                            if(ssl!=SSL_NONE)_ssl=ssl==SSL_YES;
701                            else _ssl=((ServerImpl)server).isSSL();
702                            
703                            
704                            MimeMessageAndSession msgSess;
705                            
706                            synchronized(LOCK) {
707                                    try {
708                                            msgSess = createMimeMessage(config,server.getHostName(),server.getPort(),_username,_password,_tls,_ssl);
709                                    } catch (MessagingException e) {
710                                            // listener
711                                            listener(config,server,log,e,System.nanoTime()-start);
712                                            MailException me = new MailException(e.getMessage());
713                                            me.setStackTrace(e.getStackTrace());
714                                            throw me;
715                                    }
716                                    try {
717                            SerializableObject lock = new SerializableObject();
718                            SMTPSender sender=new SMTPSender(lock,msgSess,server.getHostName(),server.getPort(),_username,_password);
719                            sender.start();
720                            SystemUtil.wait(lock, timeout);
721                            
722                            if(!sender.hasSended()) {
723                                    Throwable t = sender.getThrowable();
724                                    if(t!=null) throw Caster.toPageException(t);
725                                    
726                                    // stop when still running
727                                    try{
728                                            if(sender.isAlive())sender.stop();
729                                    }
730                                    catch(Throwable t2){}
731                                    
732                                    // after thread s stopped check send flag again
733                                    if(!sender.hasSended()){
734                                            throw new MessagingException("timeout occurred after "+(timeout/1000)+" seconds while sending mail message");
735                                    }
736                            }
737                            clean(config,attachmentz);
738                            
739                            listener(config,server,log,null,System.nanoTime()-start);
740                            break;
741                                    } 
742                        catch (Exception e) {e.printStackTrace();
743                                            if(i+1==servers.length) {
744                                                    
745                                                    listener(config,server,log,e,System.nanoTime()-start);
746                                                    MailException me = new MailException(server.getHostName()+" "+LogUtil.toMessage(e)+":"+i);
747                                                    me.setStackTrace(e.getStackTrace());
748                                                    
749                                                    throw me;
750                            }
751                                    }
752                            }
753                    }
754                    }
755                    finally {
756                    Proxy.end();
757                    }
758            }
759    
760            private void listener(ConfigWeb config,Server server, LogAndSource log, Exception e, long exe) {
761                    StringBuilder sbTos=new StringBuilder();
762                    for(int i=0;i<tos.length;i++){
763                            if(sbTos.length()>0)sbTos.append(", ");
764                            sbTos.append(tos[i].toString());
765                    }
766    
767                    if(e==null) log.info("mail","mail sended (from:"+from.toString()+"; to:"+sbTos+" subject:"+subject+")");
768                    else log.error("mail",LogUtil.toMessage(e));
769                    
770                    // listener
771                    Map<String,Object> props=new HashMap<String,Object>();
772                    props.put("attachments", this.attachmentz);
773                    props.put("bccs", this.bccs);
774                    props.put("ccs", this.ccs);
775                    props.put("charset", this.charset);
776                    props.put("from", this.from);
777                    props.put("fts", this.fts);
778                    props.put("headers", this.headers);
779                    props.put("host", server.getHostName());
780                    props.put("htmlText", this.htmlText);
781                    props.put("htmlTextCharset", this.htmlTextCharset);
782                    props.put("parts", this.parts);
783                    props.put("password", this.password);
784                    props.put("plainText", this.plainText);
785                    props.put("plainTextCharset", this.plainTextCharset);
786                    props.put("port", server.getPort());
787                    props.put("proxyData", this.proxyData);
788                    props.put("rts", this.rts);
789                    props.put("subject", this.subject);
790                    props.put("timeout", this.timeout);
791                    props.put("timezone", this.timeZone);
792                    props.put("tos", this.tos);
793                    props.put("username", this.username);
794                    props.put("xmailer", this.xmailer);
795                    ((ConfigWebImpl)config).getActionMonitorCollector()
796                            .log(config, "mail", "Mail", exe, props);
797                    
798            }
799    
800    
801            // remove all atttachements that are marked to remove
802            private static void clean(Config config, Attachment[] attachmentz) {
803                    if(attachmentz!=null)for(int i=0;i<attachmentz.length;i++){
804                            if(attachmentz[i].isRemoveAfterSend()){
805                                    Resource res = config.getResource(attachmentz[i].getAbsolutePath());
806                                    ResourceUtil.removeEL(res,true);
807                            }
808                    }
809            }
810    
811            private MimeBodyPart getHTMLText() throws MessagingException {
812                    MimeBodyPart html = new MimeBodyPart();
813                    fillHTMLText(html);
814                    return html;
815            }
816            
817            private void fillHTMLText(MimePart mp) throws MessagingException {
818                    mp.setDataHandler(new DataHandler(new StringDataSource(htmlText,TEXT_HTML ,htmlTextCharset)));
819                    mp.setHeader("Content-Transfer-Encoding", "7bit");
820                    mp.setHeader("Content-Type", TEXT_HTML+"; charset="+htmlTextCharset);
821            }
822    
823            private MimeBodyPart getPlainText() throws MessagingException {
824                    MimeBodyPart plain = new MimeBodyPart();
825                    fillPlainText(plain);
826                    return plain;
827            }
828            private void fillPlainText(MimePart mp) throws MessagingException {
829                    mp.setDataHandler(new DataHandler(new StringDataSource(plainText,TEXT_PLAIN ,plainTextCharset)));
830                    mp.setHeader("Content-Transfer-Encoding", "7bit");
831                    mp.setHeader("Content-Type", TEXT_PLAIN+"; charset="+plainTextCharset);
832            }
833            
834            private BodyPart toMimeBodyPart(MailPart part) throws MessagingException {
835                    MimeBodyPart mbp = new MimeBodyPart();
836                    mbp.setDataHandler(new DataHandler(new StringDataSource(part.getBody(),part.getType() ,part.getCharset())));
837                    //mbp.setHeader("Content-Transfer-Encoding", "7bit");
838                    //mbp.setHeader("Content-Type", TEXT_PLAIN+"; charset="+plainTextCharset);
839                    return mbp;
840            }
841    
842            /**
843             * @return the proxyData
844             */
845            public ProxyData getProxyData() {
846                    return proxyData;
847            }
848    
849            /**
850             * @param proxyData the proxyData to set
851             */
852            public void setProxyData(ProxyData proxyData) {
853                    this.proxyData = proxyData;
854            }
855    
856            /**
857             * @param ssl the ssl to set
858             */
859            public void setSSL(boolean ssl) {
860                    this.ssl = ssl?SSL_YES:SSL_NO;
861            }
862    
863            /**
864             * @param tls the tls to set
865             */
866            public void setTLS(boolean tls) {
867                    this.tls = tls?TLS_YES:TLS_NO;
868            }
869    
870            /**
871             * @return the subject
872             */
873            public String getSubject() {
874                    return subject;
875            }
876    
877            /**
878             * @return the from
879             */
880            public InternetAddress getFrom() {
881                    return from;
882            }
883    
884            /**
885             * @return the tos
886             */
887            public InternetAddress[] getTos() {
888                    return tos;
889            }
890    
891            /**
892             * @return the bccs
893             */
894            public InternetAddress[] getBccs() {
895                    return bccs;
896            }
897    
898            /**
899             * @return the ccs
900             */
901            public InternetAddress[] getCcs() {
902                    return ccs;
903            }
904    
905            public void setPart(MailPart part) {
906                    if(parts==null) parts=new ArrayList<MailPart>();
907                    parts.add(part);
908            }
909    
910    
911            public void setTimeZone(TimeZone timeZone) {
912                    this.timeZone=timeZone;
913            }
914    }