package railo.runtime.net.smtp;

import java.io.FileNotFoundException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.TimeZone;

import javax.activation.DataHandler;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimePart;

import org.apache.commons.collections.ReferenceMap;

import railo.commons.activation.ResourceDataSource;
import railo.commons.digest.MD5;
import railo.commons.io.SystemUtil;
import railo.commons.io.log.LogAndSource;
import railo.commons.io.log.LogUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.SerializableObject;
import railo.commons.lang.StringUtil;
import railo.runtime.config.Config;
import railo.runtime.config.ConfigWeb;
import railo.runtime.config.ConfigWebImpl;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.net.mail.MailException;
import railo.runtime.net.mail.MailPart;
import railo.runtime.net.mail.MailUtil;
import railo.runtime.net.mail.Server;
import railo.runtime.net.mail.ServerImpl;
import railo.runtime.net.proxy.Proxy;
import railo.runtime.net.proxy.ProxyData;
import railo.runtime.net.proxy.ProxyDataImpl;
import railo.runtime.net.smtp.SMTPConnectionPool.SessionAndTransport;
import railo.runtime.op.Caster;
import railo.runtime.spooler.mail.MailSpoolerTask;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.ListUtil;

import com.sun.mail.smtp.SMTPMessage;

public final class SMTPClient implements Serializable  {

	

	/**
	 * 
	 */
	private static final long serialVersionUID = 5227282806519740328L;
	
	private static final int SPOOL_UNDEFINED=0;
	private static final int SPOOL_YES=1;
	private static final int SPOOL_NO=2;

	private static final int SSL_NONE=0;
	private static final int SSL_YES=1;
	private static final int SSL_NO=2;

	private static final int TLS_NONE=0;
	private static final int TLS_YES=1;
	private static final int TLS_NO=2;
	
	private static final String TEXT_HTML = "text/html";
	private static final String TEXT_PLAIN = "text/plain";
	private static final SerializableObject LOCK = new SerializableObject();

	private static Map<TimeZone, SimpleDateFormat> formatters=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
	//private static final int PORT = 25; 
	
	private int spool=SPOOL_UNDEFINED;
	
	private int timeout=-1;
	
	private String plainText;
	private String plainTextCharset;
	
	private String htmlText;
	

	private String htmlTextCharset;

	private Attachment[] attachmentz;

	private String[] host;
	private String charset="UTF-8";
	private InternetAddress from;
	private InternetAddress[] tos;
	private InternetAddress[] bccs;
	private InternetAddress[] ccs;
	private InternetAddress[] rts;
	private InternetAddress[] fts;
	private String subject="";
	private String xmailer="Railo Mail";
	private Map<String,String> headers=new HashMap<String,String>();
	private int port=-1;

	private String username;
	private String password="";


	
	
	private int ssl=SSL_NONE;
	private int tls=TLS_NONE;
	
	ProxyData proxyData=new ProxyDataImpl();
	private ArrayList<MailPart> parts;

	private TimeZone timeZone;
	
	
	public static String getNow(TimeZone tz){
		tz = ThreadLocalPageContext.getTimeZone(tz);
		SimpleDateFormat df=formatters.get(tz);
		if(df==null) {
			df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z (z)",Locale.US);
			df.setTimeZone(tz);
			formatters.put(tz, df);
		}
		return df.format(new Date());
	}
	
	
	public void setSpoolenable(boolean spoolenable) {
		spool=spoolenable?SPOOL_YES:SPOOL_NO;
	}

	/**
	 * set port of the mailserver
	 * @param port
	 */
	public void setPort(int port) {
		this.port=port;
	}

	/**
	 * @param charset the charset to set
	 */
	public void setCharset(String charset) {
		this.charset = charset;
	}

	
	public static ServerImpl toServerImpl(String server,int port, String usr,String pwd) throws MailException {
		int index;
		
		// username/password
		index=server.indexOf('@');
		if(index!=-1) {
			usr=server.substring(0,index);
			server=server.substring(index+1);
			index=usr.indexOf(':');
			if(index!=-1) {
				pwd=usr.substring(index+1);
				usr=usr.substring(0,index);
			}
		}
		
		// port
		index=server.indexOf(':');
		if(index!=-1) {
			try {
				port=Caster.toIntValue(server.substring(index+1));
			} catch (ExpressionException e) {
				throw new MailException(e.getMessage());
			}
			server=server.substring(0,index);
		}
		
		
		ServerImpl srv = ServerImpl.getInstance(server, port, usr, pwd, false, false);
		return srv;
	}
	
	public void setHost(String host) throws PageException {
		if(!StringUtil.isEmpty(host,true))this.host = ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(host, ','));
	} 

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @param username the username to set
	 */
	public void setUsername(String username) {
		this.username = username;
	}
	

	public void addHeader(String name, String value) {
		headers.put(name, value);
	}

	public void addTo(InternetAddress to) {
		tos=add(tos,to);
	}

	public void addTo(Object to) throws UnsupportedEncodingException, PageException, MailException {
		InternetAddress[] tmp = MailUtil.toInternetAddresses(to);
		for(int i=0;i<tmp.length;i++) {
			addTo(tmp[i]);
		}
	}

	public void setFrom(InternetAddress from) {
		this.from=from;
	}

	public void setFrom(Object from) throws UnsupportedEncodingException, MailException, PageException {
		InternetAddress[] addrs = MailUtil.toInternetAddresses(from);
		if(addrs.length==0) return;
		setFrom(addrs[0]);
	}
	
	public void addBCC(InternetAddress bcc) {
		bccs=add(bccs,bcc);
	}

	public void addBCC(Object bcc) throws UnsupportedEncodingException, MailException, PageException {
		InternetAddress[] tmp = MailUtil.toInternetAddresses(bcc);
		for(int i=0;i<tmp.length;i++) {
			addBCC(tmp[i]);
		}
	}

	public void addCC(InternetAddress cc) {
		ccs=add(ccs,cc);
	}

	public void addCC(Object cc) throws UnsupportedEncodingException, MailException, PageException {
		InternetAddress[] tmp = MailUtil.toInternetAddresses(cc);
		for(int i=0;i<tmp.length;i++) {
			addCC(tmp[i]);
		}
	}
	
	public void addReplyTo(InternetAddress rt) {
		rts=add(rts,rt);
	}

	public void addReplyTo(Object rt) throws UnsupportedEncodingException, MailException, PageException {
		InternetAddress[] tmp = MailUtil.toInternetAddresses(rt);
		for(int i=0;i<tmp.length;i++) {
			addReplyTo(tmp[i]);
		}
	}
	
	public void addFailTo(InternetAddress ft) {
		fts=add(fts,ft);
	}

	public String getHTMLTextAsString() {
		return htmlText;
	}
	public String getPlainTextAsString() {
		return plainText;
	}

	public void addFailTo(Object ft) throws UnsupportedEncodingException, MailException, PageException {
		InternetAddress[] tmp = MailUtil.toInternetAddresses(ft);
		for(int i=0;i<tmp.length;i++) {
			addFailTo(tmp[i]);
		}
	}

	/**
	 * @param timeout the timeout to set
	 */
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}
	
	public void setSubject(String subject) {
		this.subject=subject;
	}
	
	public void setXMailer(String xmailer) {
		this.xmailer=xmailer;
	}

	/**
	 * creates a new expanded array and return it;
	 * @param oldArr
	 * @param newValue
	 * @return new expanded array
	 */
	protected static InternetAddress[] add(InternetAddress[] oldArr, InternetAddress newValue) {
		if(oldArr==null) return new InternetAddress[] {newValue};
		//else {
		InternetAddress[] tmp=new InternetAddress[oldArr.length+1];
		for(int i=0;i<oldArr.length;i++) {
			tmp[i]=oldArr[i];
		}
		tmp[oldArr.length]=newValue;	
		return tmp;
		//}
	}
	
	protected static Attachment[] add(Attachment[] oldArr, Attachment newValue) {
		if(oldArr==null) return new Attachment[] {newValue};
		//else {
		Attachment[] tmp=new Attachment[oldArr.length+1];
			for(int i=0;i<oldArr.length;i++) {
				tmp[i]=oldArr[i];
			}
			tmp[oldArr.length]=newValue;	
			return tmp;
		//}
	}

	public static class MimeMessageAndSession {
		public final MimeMessage message;
		public final SessionAndTransport session;

		public MimeMessageAndSession(MimeMessage message,SessionAndTransport session){
			this.message=message;
			this.session=session;
		}
	}
	
	
	private MimeMessageAndSession createMimeMessage(railo.runtime.config.Config config,String hostName, int port, String username, String password,
			boolean tls,boolean ssl) throws MessagingException {
		
	      Properties props = (Properties) System.getProperties().clone();
	      String strTimeout = Caster.toString(getTimeout(config));
	      
	      props.put("mail.smtp.host", hostName);
	      props.put("mail.smtp.timeout", strTimeout);
	      props.put("mail.smtp.connectiontimeout", strTimeout);
	      if(port>0){
	    	  props.put("mail.smtp.port", Caster.toString(port));
	      }
	      if(ssl)	{
              props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
	    	  props.put("mail.smtp.socketFactory.port", Caster.toString(port));
              props.put("mail.smtp.socketFactory.fallback", "false");
          }
          else {
        	  props.put("mail.smtp.socketFactory.class", "javax.net.SocketFactory");
	    	  props.remove("mail.smtp.socketFactory.port");
              props.remove("mail.smtp.socketFactory.fallback");
          }
	      Authenticator auth=null;
	      if(!StringUtil.isEmpty(username)) {
	    	  props.put("mail.smtp.auth", "true"); 
	    	  props.put("mail.smtp.starttls.enable",tls?"true":"false");
	    	  
	    	  props.put("mail.smtp.user", username);
	    	  props.put("mail.smtp.password", password);
	    	  props.put("password", password);
	    	  auth=new SMTPAuthenticator( username, password );
	      }
	      else {
	    	  props.put("mail.smtp.auth", "false"); 
	    	  props.remove("mail.smtp.starttls.enable");
	    	  
	    	  props.remove("mail.smtp.user");
	    	  props.remove("mail.smtp.password");
	    	  props.remove("password");
	      }
	      SessionAndTransport sat = SMTPConnectionPool.getSessionAndTransport(props,hash(props),auth);
	      
	// Contacts
		SMTPMessage msg = new SMTPMessage(sat.session);
		if(from==null)throw new MessagingException("you have do define the from for the mail"); 
		//if(tos==null)throw new MessagingException("you have do define the to for the mail"); 
		
		checkAddress(from,charset);
		//checkAddress(tos,charset);
		
		msg.setFrom(from);
		//msg.setRecipients(Message.RecipientType.TO, tos);
	    
		if(tos!=null){
			checkAddress(tos,charset);
			msg.setRecipients(Message.RecipientType.TO, tos);
	    }
		if(ccs!=null){
			checkAddress(ccs,charset);
	    	msg.setRecipients(Message.RecipientType.CC, ccs);
	    }
	    if(bccs!=null){
			checkAddress(bccs,charset);
	    	msg.setRecipients(Message.RecipientType.BCC, bccs);
	    }
	    if(rts!=null){
			checkAddress(rts,charset);
	    	msg.setReplyTo(rts);
	    }
	    if(fts!=null){
			checkAddress(fts,charset);
	    	msg.setEnvelopeFrom(fts[0].toString());
	    }
	    
	// Subject and headers
	    try {
			msg.setSubject(MailUtil.encode(subject, charset));
		} catch (UnsupportedEncodingException e) {
			throw new MessagingException("the encoding "+charset+" is not supported");
		}
		msg.setHeader("X-Mailer", xmailer);
		
		msg.setHeader("Date",getNow(timeZone)); 
	    //msg.setSentDate(new Date());
			
	    Multipart mp=null;
	    
		// Only HTML
		if(plainText==null) {
			if(ArrayUtil.isEmpty(attachmentz) && ArrayUtil.isEmpty(parts)){
				fillHTMLText(msg);
				setHeaders(msg,headers);
				return new MimeMessageAndSession(msg,sat);
			}
			mp = new MimeMultipart();
			mp.addBodyPart(getHTMLText());
		}
		// only Plain
		else if(htmlText==null) {
			if(ArrayUtil.isEmpty(attachmentz) && ArrayUtil.isEmpty(parts)){
				fillPlainText(msg);
				setHeaders(msg,headers);
				return new MimeMessageAndSession(msg,sat);
			}
			mp = new MimeMultipart();
			mp.addBodyPart(getPlainText());
		}
		// Plain and HTML
		else {
			mp=new MimeMultipart("alternative");
			mp.addBodyPart(getPlainText());
			mp.addBodyPart(getHTMLText());
			
			if(!ArrayUtil.isEmpty(attachmentz) || !ArrayUtil.isEmpty(parts)){
				MimeBodyPart content = new MimeBodyPart();
				content.setContent(mp);
				mp = new MimeMultipart();
				mp.addBodyPart(content);
			}
		}
		
		// parts
		if(!ArrayUtil.isEmpty(parts)){
			Iterator<MailPart> it = parts.iterator();
			if(mp instanceof MimeMultipart)
				((MimeMultipart)mp).setSubType("alternative");
			while(it.hasNext()){
				mp.addBodyPart(toMimeBodyPart(it.next()));	
			}
		}
		
		// Attachments
		if(!ArrayUtil.isEmpty(attachmentz)){
			for(int i=0;i<attachmentz.length;i++) {
				mp.addBodyPart(toMimeBodyPart(mp,config,attachmentz[i]));	
			}	
		}		
		msg.setContent(mp);
		setHeaders(msg,headers);
	    
		return new MimeMessageAndSession(msg,sat);
	}

	private static String hash(Properties props) {
		Enumeration<?> e = props.propertyNames();
		java.util.List<String> names=new ArrayList<String>();
		String str;
		while(e.hasMoreElements()){
			str=Caster.toString(e.nextElement(),null);
			if(!StringUtil.isEmpty(str) && str.startsWith("mail.smtp."))
				names.add(str);
			
		}
		Collections.sort(names);
		StringBuilder sb=new StringBuilder();
		Iterator<String> it = names.iterator();
		while(it.hasNext()){
			str=it.next();
			sb.append(str).append(':').append(props.getProperty(str)).append(';');
		}
		str=sb.toString();
		return MD5.getDigestAsString(str,str);
		
	}

	private static void setHeaders(SMTPMessage msg, Map<String,String> headers) throws MessagingException {
		Iterator<Entry<String, String>> it = headers.entrySet().iterator();
		Entry<String, String> e;
	    while(it.hasNext()) {
	    	e = it.next();
	    	msg.setHeader(e.getKey(),e.getValue());
	    }
	}

	private void checkAddress(InternetAddress[] ias,String charset) { // DIFF 23
		for(int i=0;i<ias.length;i++) {
			checkAddress(ias[i], charset);
		}
	}
	private void checkAddress(InternetAddress ia,String charset) { // DIFF 23
		try {
			if(!StringUtil.isEmpty(ia.getPersonal())) {
				String personal = MailUtil.encode(ia.getPersonal(), charset);
				if(!personal.equals(ia.getPersonal()))
					ia.setPersonal(personal);
			}
		} catch (UnsupportedEncodingException e) {}
	}

	/**
	 * @param plainText
	 */
	public void setPlainText(String plainText) {
		this.plainText=plainText;
		this.plainTextCharset=charset;
	}
	
	/**
	 * @param plainText
	 * @param plainTextCharset
	 */
	public void setPlainText(String plainText, String plainTextCharset) {
		this.plainText=plainText;
		this.plainTextCharset=plainTextCharset;
	}
	
	/**
	 * @param htmlText
	 */
	public void setHTMLText(String htmlText) {
		this.htmlText=htmlText;
		this.htmlTextCharset=charset;
	}



	public boolean hasHTMLText() {
		return htmlText!=null;
	}

	public boolean hasPlainText() {
		return plainText!=null;
	}
	
	/**
	 * @param htmlText
	 * @param htmlTextCharset
	 */
	public void setHTMLText(String htmlText, String htmlTextCharset) {
		this.htmlText=htmlText;
		this.htmlTextCharset=htmlTextCharset;
	}
	
	public void addAttachment(URL url) {
		Attachment mbp = new Attachment(url);
		attachmentz=add(attachmentz, mbp);
	}

	public void addAttachment(Resource resource, String type, String disposition, String contentID,boolean removeAfterSend) {
		Attachment att = new Attachment(resource, type, disposition, contentID,removeAfterSend);
		attachmentz=add(attachmentz, att);
	}
	
	public MimeBodyPart toMimeBodyPart(Multipart mp, railo.runtime.config.Config config,Attachment att) throws MessagingException  {
		
		MimeBodyPart mbp = new MimeBodyPart();
		
		// set Data Source
		String strRes = att.getAbsolutePath();
		if(!StringUtil.isEmpty(strRes)){
			
			mbp.setDataHandler(new DataHandler(new ResourceDataSource(config.getResource(strRes))));
		}
		else mbp.setDataHandler(new DataHandler(new URLDataSource2(att.getURL())));
		
		mbp.setFileName(att.getFileName());
		if(!StringUtil.isEmpty(att.getType())) mbp.setHeader("Content-Type", att.getType());
		if(!StringUtil.isEmpty(att.getDisposition())){
			mbp.setDisposition(att.getDisposition());
			if(mp instanceof MimeMultipart)
				((MimeMultipart)mp).setSubType("related");
			
		}
		if(!StringUtil.isEmpty(att.getContentID()))mbp.setContentID(att.getContentID());
			
		return mbp;
	}
	
	/**
	 * @param file
	 * @throws MessagingException
	 * @throws FileNotFoundException 
	 */
	public void addAttachment(Resource file) throws MessagingException {
		addAttachment(file,null,null,null,false);
	}
	

	
	
	public void send(ConfigWeb config) throws MailException {
		if(ArrayUtil.isEmpty(config.getMailServers()) && ArrayUtil.isEmpty(host))
			throw new MailException("no SMTP Server defined");
		
		if(plainText==null && htmlText==null)
			throw new MailException("you must define plaintext or htmltext");
		
		///if(timeout<1)timeout=config.getMailTimeout()*1000;
		if(spool==SPOOL_YES || (spool==SPOOL_UNDEFINED && config.isMailSpoolEnable())) {
        	config.getSpoolerEngine().add(new MailSpoolerTask(this));
        }
		else
			_send(config);
	}
	

	public void _send(railo.runtime.config.ConfigWeb config) throws MailException {
		long start=System.nanoTime();
		long _timeout = getTimeout(config);
		try {

        	Proxy.start(proxyData);
		LogAndSource log = config.getMailLogger();
		// Server
        Server[] servers = config.getMailServers();
        if(host!=null) {
        	int prt;
        	String usr,pwd;
        	ServerImpl[] nServers = new ServerImpl[host.length];
        	for(int i=0;i<host.length;i++) {
        		usr=null;pwd=null;
        		prt=ServerImpl.DEFAULT_PORT;
        		
        		if(port>0)prt=port;
        		if(!StringUtil.isEmpty(username))	{
        			usr=username;
        			pwd=password;
        		}
        		
        		nServers[i]=toServerImpl(host[i],prt,usr,pwd);
				if(ssl==SSL_YES) nServers[i].setSSL(true);
        		if(tls==TLS_YES) nServers[i].setTLS(true);
        			
        	}
        	servers=nServers;
        }
		if(servers.length==0) {
			//return;
			throw new MailException("no SMTP Server defined");
		}
		
		boolean _ssl,_tls;
		for(int i=0;i<servers.length;i++) {

			Server server = servers[i];
			String _username=null,_password="";
			//int _port;
			
			// username/password
			
			if(server.hasAuthentication()) {
				_username=server.getUsername();
				_password=server.getPassword();
			}
			
			
			// tls
			if(tls!=TLS_NONE)_tls=tls==TLS_YES;
			else _tls=((ServerImpl)server).isTLS();
	
			// ssl
			if(ssl!=SSL_NONE)_ssl=ssl==SSL_YES;
			else _ssl=((ServerImpl)server).isSSL();
			
			
			MimeMessageAndSession msgSess;
			
			synchronized(LOCK) {
				try {
					msgSess = createMimeMessage(config,server.getHostName(),server.getPort(),_username,_password,_tls,_ssl);
				} catch (MessagingException e) {
					// listener
					listener(config,server,log,e,System.nanoTime()-start);
					MailException me = new MailException(e.getMessage());
					me.setStackTrace(e.getStackTrace());
					throw me;
				}
				try {
	            	SerializableObject lock = new SerializableObject();
	            	SMTPSender sender=new SMTPSender(lock,msgSess,server.getHostName(),server.getPort(),_username,_password);
            		sender.start();
            		SystemUtil.wait(lock, _timeout);
            		
            		if(!sender.hasSended()) {
                		Throwable t = sender.getThrowable();
                		if(t!=null) throw Caster.toPageException(t);
                		
                		// stop when still running
                		try{
                			if(sender.isAlive())sender.stop();
                		}
                		catch(Throwable t2){}
                		
                		// after thread s stopped check send flag again
                		if(!sender.hasSended()){
                			throw new MessagingException("timeout occurred after "+(_timeout/1000)+" seconds while sending mail message");
                		}
                	}
                	clean(config,attachmentz);
                	
                	listener(config,server,log,null,System.nanoTime()-start);
                	break;
				} 
	            catch (Exception e) {e.printStackTrace();
					if(i+1==servers.length) {
						
						listener(config,server,log,e,System.nanoTime()-start);
						MailException me = new MailException(server.getHostName()+" "+LogUtil.toMessage(e)+":"+i);
						me.setStackTrace(e.getStackTrace());
						
						throw me;
	                }
				}
			}
		}
		}
		finally {
        	Proxy.end();
		}
	}

	private void listener(ConfigWeb config,Server server, LogAndSource log, Exception e, long exe) {
		StringBuilder sbTos=new StringBuilder();
		for(int i=0;i<tos.length;i++){
			if(sbTos.length()>0)sbTos.append(", ");
			sbTos.append(tos[i].toString());
		}

		if(e==null) log.info("mail","mail sended (from:"+from.toString()+"; to:"+sbTos+" subject:"+subject+")");
		else log.error("mail",LogUtil.toMessage(e));
		
		// listener
		
		Map<String,Object> props=new HashMap<String,Object>();
		props.put("attachments", this.attachmentz);
		props.put("bccs", this.bccs);
		props.put("ccs", this.ccs);
		props.put("charset", this.charset);
		props.put("from", this.from);
		props.put("fts", this.fts);
		props.put("headers", this.headers);
		props.put("host", server.getHostName());
		props.put("htmlText", this.htmlText);
		props.put("htmlTextCharset", this.htmlTextCharset);
		props.put("parts", this.parts);
		props.put("password", this.password);
		props.put("plainText", this.plainText);
		props.put("plainTextCharset", this.plainTextCharset);
		props.put("port", server.getPort());
		props.put("proxyData", this.proxyData);
		props.put("rts", this.rts);
		props.put("subject", this.subject);
		props.put("timeout", getTimeout(config));
		props.put("timezone", this.timeZone);
		props.put("tos", this.tos);
		props.put("username", this.username);
		props.put("xmailer", this.xmailer);
		((ConfigWebImpl)config).getActionMonitorCollector()
			.log(config, "mail", "Mail", exe, props);
		
	}


	private long getTimeout(Config config) {
		return timeout>0?timeout:config.getMailTimeout()*1000L;
	}


	// remove all atttachements that are marked to remove
	private static void clean(Config config, Attachment[] attachmentz) {
		if(attachmentz!=null)for(int i=0;i<attachmentz.length;i++){
			if(attachmentz[i].isRemoveAfterSend()){
				Resource res = config.getResource(attachmentz[i].getAbsolutePath());
				ResourceUtil.removeEL(res,true);
			}
		}
	}

	private MimeBodyPart getHTMLText() throws MessagingException {
		MimeBodyPart html = new MimeBodyPart();
		fillHTMLText(html);
		return html;
	}
	
	private void fillHTMLText(MimePart mp) throws MessagingException {
		mp.setDataHandler(new DataHandler(new StringDataSource(htmlText,TEXT_HTML ,htmlTextCharset)));
		mp.setHeader("Content-Transfer-Encoding", "7bit");
		mp.setHeader("Content-Type", TEXT_HTML+"; charset="+htmlTextCharset);
	}

	private MimeBodyPart getPlainText() throws MessagingException {
		MimeBodyPart plain = new MimeBodyPart();
		fillPlainText(plain);
		return plain;
	}
	private void fillPlainText(MimePart mp) throws MessagingException {
		mp.setDataHandler(new DataHandler(new StringDataSource(plainText,TEXT_PLAIN ,plainTextCharset)));
		mp.setHeader("Content-Transfer-Encoding", "7bit");
		mp.setHeader("Content-Type", TEXT_PLAIN+"; charset="+plainTextCharset);
	}
	
	private BodyPart toMimeBodyPart(MailPart part) throws MessagingException {
		MimeBodyPart mbp = new MimeBodyPart();
		mbp.setDataHandler(new DataHandler(new StringDataSource(part.getBody(),part.getType() ,part.getCharset())));
		//mbp.setHeader("Content-Transfer-Encoding", "7bit");
		//mbp.setHeader("Content-Type", TEXT_PLAIN+"; charset="+plainTextCharset);
		return mbp;
	}

	/**
	 * @return the proxyData
	 */
	public ProxyData getProxyData() {
		return proxyData;
	}

	/**
	 * @param proxyData the proxyData to set
	 */
	public void setProxyData(ProxyData proxyData) {
		this.proxyData = proxyData;
	}

	/**
	 * @param ssl the ssl to set
	 */
	public void setSSL(boolean ssl) {
		this.ssl = ssl?SSL_YES:SSL_NO;
	}

	/**
	 * @param tls the tls to set
	 */
	public void setTLS(boolean tls) {
		this.tls = tls?TLS_YES:TLS_NO;
	}

	/**
	 * @return the subject
	 */
	public String getSubject() {
		return subject;
	}

	/**
	 * @return the from
	 */
	public InternetAddress getFrom() {
		return from;
	}

	/**
	 * @return the tos
	 */
	public InternetAddress[] getTos() {
		return tos;
	}

	/**
	 * @return the bccs
	 */
	public InternetAddress[] getBccs() {
		return bccs;
	}

	/**
	 * @return the ccs
	 */
	public InternetAddress[] getCcs() {
		return ccs;
	}

	public void setPart(MailPart part) {
		if(parts==null) parts=new ArrayList<MailPart>();
		parts.add(part);
	}


	public void setTimeZone(TimeZone timeZone) {
		this.timeZone=timeZone;
	}
}
