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