SendMail.java

package sk.iway.iwcm;

import sk.iway.iwcm.common.DocTools;
import sk.iway.iwcm.helpers.MailHelper;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.io.IwcmFsDB;
import sk.iway.iwcm.system.context.ContextFilter;
import sk.iway.iwcm.system.multidomain.MultiDomainFilter;
import sk.iway.iwcm.utils.Pair;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.activation.URLDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.servlet.http.HttpServletRequest;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.ElementIterator;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import java.io.*;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.*;

//import sk.iway.intranet.dms.DmsNotify;

/**
 *  Odosielanie emailu, priklad telnet spojenia:

EHLO tau19.iway.sk
MAIL FROM:noreply@interway.sk
RCPT TO:veronika.husarova@employment.gov.sk
DATA
From:noreply@interway.sk
To:veronika.husarova@employment.gov.sk
Subject:test

Test
.
QUIT

 *
 *@Title        magma-web
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2002
 *@author       $Author: jeeff $
 *@version      $Revision: 1.13 $
 *@created      Nede�e, 2003, febru�r 2
 *@modified     $Date: 2004/03/23 19:23:05 $
 */
public class SendMail
{

	/**
     * Comment for <code>AUTOREPLY_DAEMON</code>
     */
    	public static final String AUTOREPLY_SUBJECT = "AUTOREPLY-";
	public static final String EMAIL_PROTECTION_SENDER_KEY = "emailProtectionSenderEmail";
	public static final String EMAIL_PROTECTION_SENDER_NAME_KEY = "emailProtectionSenderName";

	public static boolean send(String fromName, String fromEmail, String toEmail, String subject, String text)
	{
		return (send(fromName, fromEmail, toEmail, null, null, subject, text, null));
	}

	public static boolean send(String fromName, String fromEmail, String toEmail, String subject, String text, HttpServletRequest request)
	{
		String baseHref = Tools.getBaseHref(request)+"/";
		return (send(fromName, fromEmail, toEmail, null, null, null, subject, text, baseHref, null));
	}

	public static boolean send(String fromName, String fromEmail, String toEmail, String subject, String text, String attachments)
	{
		return (send(fromName, fromEmail, toEmail, null, null, subject, text, attachments));
	}

	public static boolean send(String fromName, String fromEmail, String toEmail, String ccEmail, String bccEmail, String subject, String text, String attachments)
	{
		return(send(fromName, fromEmail, toEmail, null, ccEmail, bccEmail, subject, text, null, attachments));
	}

	public static boolean send(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String attachmentsList)
	{
		return sendCapturingException(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, attachmentsList).first;
	}

	 public static Pair<Boolean, Exception> sendCapturingException(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String attachmentsList)
	 {
		  return sendCapturingException(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, attachmentsList, true);
	 }

	 public static Pair<Boolean, Exception> sendCapturingException(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String attachmentsList, boolean sendLaterWhenException)
	 {
		  return sendCapturingException(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, attachmentsList, sendLaterWhenException, true);
	 }

	/**
	 * Odoslanie emailu
	 * @param senderName - meno odosielatela emailu
	 * @param senderEmail - email odosielatela emailu
	 * @param recipientEmail - email prijemcu
	 * @param replyTo - email pre pole replyTo, alebo null
	 * @param subject - predmet
	 * @param message - body emailu, moze byt v HTML formate, vratane odkazov na obrazky a linky
	 * @param baseHref - base HTTP adresa (aby bolo mozne nalinkovat obrazky a spravit relativne linky)
	 * @param attachmentsList - zoznam priloh vo formate url_adresa1;nazov_suboru_do_emailu1;url_adresa2;nazov_suboru_do_emailu2;
	 * @param sendLaterWhenException - ak je true, tak pri exception pri odosielani, ulozi e-mail pre neskorsie odoslanie
	 * @param writeToAuditLog - ak je false, tak sa posle len e-mail, ale nezapise sa nic do auditlogu
	 * @return true, ak sa email podarilo odoslat, inak false
	 * @throws Exception
	 */
	public static Pair<Boolean, Exception> sendCapturingException(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String attachmentsList, boolean sendLaterWhenException, boolean writeToAuditLog) {
		MailHelper mailHelper = new MailHelper()
				.setFromName(senderName)
				.setFromEmail(senderEmail)
				.setToEmail(recipientEmail)
				.setReplyTo(replyTo)
				.setCcEmail(ccEmail)
				.setBccEmail(bccEmail)
				.setSubject(subject)
				.setMessage(message)
				.setBaseHref(baseHref)
				.setAttachments(attachmentsList)
				.setSendLaterWhenException(sendLaterWhenException)
				.setWriteToAuditLog(writeToAuditLog);

		return sendCapturingException(mailHelper);
	}

	public static Pair<Boolean, Exception> sendCapturingException(MailHelper mailHelper)
	{
		String senderName = mailHelper.getFromName();
		String senderEmail = mailHelper.getFromEmail();
		String recipientEmail = mailHelper.getToEmail();
		String replyTo = mailHelper.getReplyTo();
		String ccEmail = mailHelper.getCcEmail();
		String bccEmail = mailHelper.getBccEmail();
		String subject = mailHelper.getSubject();
		String message = mailHelper.getMessage();
		String baseHref = mailHelper.getBaseHref();
		String attachmentsList = mailHelper.getAttachments();
		boolean sendLaterWhenException = mailHelper.isSendLaterWhenException();
		boolean writeToAuditLog = mailHelper.isWriteToAuditLog();
		List< Pair<String, String> > headers = mailHelper.getHeaders();

		if ("false".equals(Constants.getString("useSMTPServer")) && writeToAuditLog) // mail neodosleme ale ulozime do db pre \odoslanie na inom node, okrem pripadu, ze potrebujeme odoslat chybu sposobenu pri dosiahnuti maximalenho poctu DB spojeni
		{
			Logger.debug(SendMail.class, "useSMTPServer=false -> sending later. " );
			boolean result = sendLater(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, null, null, attachmentsList);
			return Pair.of(result, null);
		}

		//debugMail
		if (Tools.isNotEmpty(Constants.getString("debugEmail")))
		{
			Logger.debug(SendMail.class, "debugEmail="+Constants.getString("debugEmail"));
			recipientEmail = Constants.getString("debugEmail");
		}
		//pridanie ContextPath pre admin cast (ak je nastavene)
		if (Tools.isNotEmpty(Constants.getString("contextPathAdmin")) && message.indexOf("://cms")!=-1)
		{
			message = ContextFilter.addContextPath(Constants.getString("contextPathAdmin"), message);
			subject = ContextFilter.addContextPath(Constants.getString("contextPathAdmin"), subject);
		}

		Prop prop = Prop.getInstance(Constants.getString("defaultLanguage"));

		//vynimka helpdesk@websupport.sk je kvoli Cloudu a notifikaciam na WebSupport kedy nam odmietaju emaili z noreply@webjet.eu spracovat
		if (Tools.isEmail(Constants.getString(EMAIL_PROTECTION_SENDER_KEY)) && "helpdesk@websupport.sk".equals(recipientEmail)==false)
		{
			String from = Constants.getString(EMAIL_PROTECTION_SENDER_KEY);
			String oldSender = senderEmail;
			senderEmail = from;

			String defaultName = Constants.getString(EMAIL_PROTECTION_SENDER_NAME_KEY);
			if (Tools.isNotEmpty(defaultName)) {
				if (defaultName.equalsIgnoreCase("same-as-email")) {
					senderName = from;
				}
				else {
					senderName = defaultName;
				}
			}

			if (!from.equals(oldSender))
			{
				if (Tools.isEmpty(replyTo))
					replyTo = oldSender;
				//else - replyTo nemoze obsahovat viacero email adries!
				//	replyTo += ","+oldSender;
			}
		}

		RequestBean requestBean = SetCharacterEncodingFilter.getCurrentRequestBean();
		if (requestBean != null)
		{
			if (Tools.isEmpty(baseHref)) baseHref = requestBean.getBaseHref();
		}
		if (Tools.isEmpty(baseHref)) try { baseHref = Tools.getBaseHref(null); } catch (Exception ex) {}

		//ak je <img src='xxxx' tak sa to nezobrazi v mozille
		try
		{
			int start = message.indexOf("src='");
			int end;
			while (start != -1)
			{
				end = message.indexOf("'", start+6);
				if (end > start)
				{
					message = message.substring(0, start) + "src=\"" + message.substring(start+5, end) + "\"" + message.substring(end+1);
					//Logger.println(this,message);
				}
				else
				{
					//ukonci cyklus
					break;
				}
			}
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}

		if (baseHref!=null && baseHref.length()>8)
		{
			//uprav to z adresy http://www.interway.sk/nieco/stranka.html na http://www.interway.sk
			int i = baseHref.indexOf('/', 9);
			if (i!=-1)
			{
				baseHref = baseHref.substring(0, i);
				Logger.println(SendMail.class,"baseHref: " + baseHref);
			}
		}

		//oprav linky
		message = SendMail.createAbsolutePath(message, baseHref);

		Properties props = System.getProperties();
		props.put("mail.smtp.host", Constants.getString("smtpServer"));
        props.put("mail.mime.charset", SetCharacterEncodingFilter.getEncoding());

		Session ms = getSession(props);


		try
		{
			//ak nie je zadany odosielatel, domyslime si ho
			if (Tools.isEmpty(senderEmail))
			{
				senderEmail = recipientEmail;
			}

			//uprav email formatu lubos@balat.sk,pokus@interway.sk pretoze
			//niekedy je sender rovnaky ako prijemca
			senderEmail = getFirstEmailAddress(senderEmail);
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}

		if (subject.indexOf("WebJET restart")==-1)
		{

			//Logger.println(this,"SendMailMultipart from:"+senderName+" <"+senderEmail+"> to:"+recipientEmail);
			Logger.debug(SendMail.class,"SendMail["+Constants.getInstallName()+"]: sending email from: " + senderEmail + " to: " + recipientEmail+" subject: "+subject+"|");

			if ("cloud".equals(Constants.getInstallName()))
			{
				Logger.debug(SendMail.class, "email from:" + senderEmail + " to:" + recipientEmail+" subject:"+subject + " body:"+message);
			}

			if(writeToAuditLog)
			{
				 if ("cloud".equals(Constants.getInstallName()) && message.indexOf("Heslo:")==-1 && message.indexOf("Password:")==-1)
				 {
					  //pre cloud (mimo dmailu) logujeme aj telo emailu
					  Adminlog.add(Adminlog.TYPE_SENDMAIL, "email from:" + senderEmail + " to:" + recipientEmail+" subject:"+subject + " body:"+message, -1, -1);
				 }
				 else
				 {
					  Adminlog.add(Adminlog.TYPE_SENDMAIL, "email from:" + senderEmail + " to:" + recipientEmail+" subject:"+subject, -1, -1);
				 }
			}
		}

		try
		{
			Message mes = new MimeMessage(ms);


			if (subject.startsWith(AUTOREPLY_SUBJECT))
			{
			    mes.setHeader("X-Autoreply",AUTOREPLY_SUBJECT);
			}

			if (headers != null && headers.size() > 0)
			{
				for (Pair<String, String> header : headers)
				{
					mes.setHeader(header.getFirst(), header.getSecond());
				}
			}

			if (senderEmail.length() > 0)
			{
				mes.setFrom(new InternetAddress(senderEmail, senderName));
			}
			else
			{
				mes.setFrom(new InternetAddress("bez.emailu@interway.sk"));
			}

			//mes.setFrom(new InternetAddress(senderEmail, DB.internationalToEnglish(senderName)));
			mes.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail, false));
			try
			{
				if (replyTo != null && replyTo.indexOf('@') != -1)
				{
					InternetAddress[] rt = new InternetAddress[1];
					replyTo = getFirstEmailAddress(replyTo);
					rt[0] = new InternetAddress(DB.internationalToEnglish(replyTo));
					mes.setReplyTo(rt);
				}
				if (ccEmail != null && ccEmail.indexOf('@')!=-1)
				{
					InternetAddress[] ccAddrs = InternetAddress.parse(ccEmail, false);
					mes.setRecipients(Message.RecipientType.CC, ccAddrs);
				}
				if (bccEmail != null && bccEmail.indexOf('@')!=-1)
				{
					InternetAddress[] bccAddrs = InternetAddress.parse(bccEmail, false);
					mes.setRecipients(Message.RecipientType.BCC, bccAddrs);
				}
			}
			catch (Exception ex)
			{
				sk.iway.iwcm.Logger.error(ex);
			}

			String emailEncoding = Constants.getString("emailEncoding");
			if (Tools.isEmpty(emailEncoding))
			{
				emailEncoding = SetCharacterEncodingFilter.getEncoding();
			}

			mes.setSubject(MimeUtility.encodeText(subject, emailEncoding, null));
			mes.setSentDate(new java.util.Date());


			if (isHtmlContent(message) || Tools.isNotEmpty(attachmentsList))
			{
				Multipart mpart = new MimeMultipart("mixed");
				Multipart attachments = new MimeMultipart();
				Multipart related = new MimeMultipart();
				message = putInlineAttachments(attachments, related, message, baseHref);

				boolean attachFiles = true;
				String serverPath;
				String fileName;
				StringTokenizer st = null;
				long size = 0;
				if (Tools.isNotEmpty(attachmentsList))   //skontroluj normalne attachmenty
				{
					st = new StringTokenizer(attachmentsList, ";");

					IwcmFile file = null;
					while (st.hasMoreTokens() && attachFiles)   //zisti velkosti priloh
					{
						serverPath = st.nextToken();
						if (st.hasMoreTokens())
						{
							file = IwcmFile.fromVirtualPath(serverPath);
							//try absolute path otherwise
							if (!file.exists()) file = new IwcmFile(serverPath);

							size += file.length();
							if (size > Constants.getLong("maxSizeOfAttachments"))
							{
								attachFiles = false;   //ak je velkost prilohy vacsia ako stanovena hranica, prilohy k mailu nepripojim
							}
						}

					}
				}
				if (!attachFiles)
				{
					message += prop.getText("email.too_large_attachments");
				}

				// create wrap if we have related attachments - images in html
				if (related.getCount() > 0)
				{
					Logger.println(SendMail.class, "mam nejake related, posielam");
					putMessagePart(mpart, message, related);
				} else
				{
					putMessagePart(mpart, message, null);
				}
				for (int i = 0; i < attachments.getCount(); i++)
				{
					mpart.addBodyPart(attachments.getBodyPart(i));
				}
				//nahod normalne attachmenty
				if (Tools.isNotEmpty(attachmentsList))
				{
					if (attachFiles)
					{
						st = new StringTokenizer(attachmentsList, ";");
						while (st.hasMoreTokens())
						{
							serverPath = st.nextToken();
							if (st.hasMoreTokens())
							{
								fileName = st.nextToken();
								SendMail.attFile(serverPath, fileName, mpart);
							}
						}
					}
				}

				mes.setContent(mpart);
			}
			else
			{
				//je to plain text email
				mes.setContent(message, "text/plain; charset="+emailEncoding);
			}

			try
			{
				if (Constants.getBoolean("sendMailSaveEmail")) {
					saveEmailToFile(mes);
				}
				else
				{
					Transport.send(mes);
				}
				Logger.debug(SendMail.class,"email odoslany");
				return Pair.of(true, null);
			}
			catch (Exception ex)
			{
				if (subject.indexOf("WebJET restart")==-1)
				{
					sk.iway.iwcm.Logger.error(ex);
					Adminlog.add(Adminlog.TYPE_SENDMAIL, "ERROR sending email (from: "+senderEmail+", to: "+recipientEmail+", cc: "+ccEmail+", bcc: "+bccEmail+", subject: "+subject+") ex:"+ex.getMessage()+"\n\n"+Logger.getStackTrace(ex), -1, -1);
					if (sendLaterWhenException) //mail ulozim pre neskorsie poslanie
					{
						Logger.debug(SendMail.class, "sendLaterWhenException=true -> sending later." );
						sendLater(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, null, null, attachmentsList);
					}
				}
				return Pair.of(false, ex);
			}
		}
		catch (Exception e)
		{
			if (subject.indexOf("WebJET restart")==-1)
			{
				sk.iway.iwcm.Logger.error(e);
                Adminlog.add(Adminlog.TYPE_SENDMAIL, "ERROR sending email (from: "+senderEmail+", to: "+recipientEmail+", cc: "+ccEmail+", bcc: "+bccEmail+", subject: "+subject+") ex:"+e.getMessage()+"\n\n"+Logger.getStackTrace(e), -1, -1);
			}
			return Pair.of(false, e);
		}
	}

	private static void saveEmailToFile(Message mes) {
		String sendMailSaveEmailPath = Constants.getString("sendMailSaveEmailPath", "");
		if (Tools.isEmpty(sendMailSaveEmailPath)) {
			Logger.debug(SendMail.class, "sendMailSaveEmailPath is not configured");
			return;
		}

		Logger.debug(SendMail.class, String.format("Email neodosielam, zapisujem do: %s", sendMailSaveEmailPath));
		String realPath = Tools.getRealPath(sendMailSaveEmailPath);
		if (!realPath.endsWith("/")) {
			realPath += "/";
		}

		File file = new File(realPath + new Date().getTime() + ".eml");
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			mes.writeTo(fileOutputStream);
			fileOutputStream.close();

			Logger.debug(SendMail.class, "Email zapisany");
		} catch (IOException | MessagingException e) {
			sk.iway.iwcm.Logger.error(e);
		}
	}

	public static boolean sendLater(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String date, String time)
	{
		return sendLater(senderName, senderEmail, recipientEmail, replyTo, ccEmail, bccEmail, subject, message, baseHref, date, time,null);
	}

	/**
	 * Oneskorene odoslanie emailu
	 * @param senderName
	 * @param senderEmail
	 * @param recipientEmail
	 * @param replyTo
	 * @param subject
	 * @param message
	 * @param baseHref
	 * @param date
	 * @param time
	 * @return
	 */
	public static boolean sendLater(String senderName, String senderEmail, String recipientEmail, String replyTo, String ccEmail, String bccEmail, String subject, String message, String baseHref, String date, String time,String attachments)
	{
		//pridanie ContextPath pre admin cast (ak je nastavene)
		if (Tools.isNotEmpty(Constants.getString("contextPathAdmin")) && message.indexOf("://cms")!=-1)
		{
			message = ContextFilter.addContextPath(Constants.getString("contextPathAdmin"), message);
			subject = ContextFilter.addContextPath(Constants.getString("contextPathAdmin"), subject);
		}

		//uloz si parametre do DB a posli to v stanovenom case
		try
		{
			Connection db_conn = DBPool.getConnection();
			try
			{
				PreparedStatement ps = db_conn.prepareStatement("INSERT INTO emails (recipient_email, recipient_name, sender_name, sender_email, subject, url, attachments, retry, sent_date, created_by_user_id, create_date, send_at, message, reply_to, cc_email, bcc_email) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
				try
				{
					int counter = 1;
					ps.setString(counter++, recipientEmail);
					ps.setString(counter++, recipientEmail);
					ps.setString(counter++, senderName);
					ps.setString(counter++, senderEmail);
					ps.setString(counter++, subject);
					ps.setString(counter++, (baseHref == null) ? "" : baseHref);
					if (Tools.isEmpty(attachments))
					{
						ps.setString(counter++, null);
					}
					else
					{
						ps.setString(counter++, attachments);
					}
					ps.setInt(counter++, 0);
					ps.setNull(counter++, Types.TIMESTAMP);
					ps.setInt(counter++, -1);
					ps.setTimestamp(counter++, new Timestamp(Tools.getNow()));
					if (Tools.isNotEmpty(date) && Tools.isNotEmpty(time))
					{
						ps.setTimestamp(counter++, new Timestamp(DB.getTimestamp(date, time)));
					}
					else
					{
						ps.setNull(counter++, Types.TIMESTAMP);
					}
					DB.setClob(ps, counter++, message);
					ps.setString(counter++, replyTo);
					ps.setString(counter++, ccEmail);
					ps.setString(counter++, bccEmail);
					ps.execute();
				}
				finally { ps.close(); }
			}
			finally { db_conn.close(); }
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
			Adminlog.add(Adminlog.TYPE_SENDMAIL, "ERROR sending email later ex:"+ex.getMessage(), -1, -1);
		}

		return(true);
	}

	/**
	 * Ziska zoznam inline priloh (obrazkov)
	 * @param htmlCode
	 * @return
	 */
	public static List<String> getInlineAttachments(String htmlCode)
	{
		//Logger.println(this,"Getting INLINE attachments from mail");
		EditorKit kit = new HTMLEditorKit();
		Document doc = kit.createDefaultDocument();
		// The Document class does not yet
		// handle charset's properly.
		doc.putProperty("IgnoreCharsetDirective", Boolean.TRUE);

		List<String> att = new ArrayList<>();
		try
		{
			// Create a reader on the HTML content.
			Reader rd = new StringReader(htmlCode);
			// Parse the HTML.
			kit.read(rd, doc, 0);
			// Iterate through the elements
			// of the HTML document.
			ElementIterator it = new ElementIterator(doc);
			javax.swing.text.Element elem;
			while ((elem = it.next()) != null)
			{
				if (elem.getName().equals("img"))
				{
					String src = (String) elem.getAttributes().getAttribute(HTML.Attribute.SRC);
					if (Tools.isEmpty(src))
					{
						continue;
					}
					//Logger.println(this,src);
					att.add(src);
				}
				//jeeff: test na atribut background
				if (elem.getAttributes().getAttribute(HTML.Attribute.BACKGROUND) != null)
				{
					String src = (String) elem.getAttributes().getAttribute(HTML.Attribute.BACKGROUND);
					if (Tools.isEmpty(src))
					{
						continue;
					}
					//Logger.println(this,src);
					att.add(src);
				}
			}
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
		return(att);
	}

	/**
	 * Prida do emailu Inline obrazky
	 * @param atts
	 * @param related
	 * @param message
	 * @param baseHref
	 * @return
	 * @throws Exception
	 */
	private static String putInlineAttachments(Multipart atts, Multipart related, String message, String baseHref) throws Exception
	{
		String rt = message;
		try
		{
			List<String> att = getInlineAttachments(message);

			Iterator<String> iter = att.iterator();
			String url;
			IwcmFile f;
			FileDataSource fds;
			URL u;
			URLDataSource uds;
			MimeBodyPart at;
			while (iter.hasNext())
			{
				url = iter.next();
				if (url.startsWith("http"))
				{
					//url = sk.iway.URLPathEncoder.encode(url);
					url = Tools.replace(url, " ", "%20");

					//inline attachment
					String downloadUrl = url;
					if (url.indexOf("http")==-1)
					{
						downloadUrl = baseHref + url;
					}
					downloadUrl = Tools.natUrl(downloadUrl);
					u = new URL(downloadUrl);

					Logger.println(SendMail.class,"ATT url: " + downloadUrl);
					uds = new URLDataSource(u);
					at = new MimeBodyPart();
					at.setDataHandler(new DataHandler(uds));
				}
				else
				{
					if (url.charAt(0)!='/')
					{
						url = "/" + url; //NOSONAR
					}

					//ak mame multidomain fixni cesty
					String localUrl = url;
					//tento try neodstranujte, moze nastat indexOufOfBounds v casti baseHref.indexOf
					try
					{
						if (Tools.isNotEmpty(baseHref))
						{
							String domain = baseHref.substring(baseHref.indexOf("://"));
							if (domain.indexOf(':')!=-1) domain = domain.substring(0, domain.indexOf(':'));
							localUrl = MultiDomainFilter.rewriteUrlToLocal(url, MultiDomainFilter.getDomainAlias(domain));
							Logger.debug(SendMail.class, "Trying to fix multidomain: url="+url+" local="+localUrl+" domain="+domain+" basehref="+baseHref);
						}
					}
					catch (Exception ex)
					{
						sk.iway.iwcm.Logger.error(ex);
					}

					f = new IwcmFile((Tools.getRealPath(localUrl)));

					if (IwcmFsDB.useDBStorage(localUrl) && f.exists())
					{
						File tempFile = new File(IwcmFsDB.getTempFilePath(f.getPath()));
						if (tempFile.exists()==false || tempFile.lastModified() > f.lastModified())
						{
							IwcmFsDB.writeFileToDisk(new File(f.getPath()), tempFile);
						}
					}

					if (f.exists() && f.canRead() && f.isFile())
					{
						Logger.println(SendMail.class,"ATT file: " + url + " path="+f.getAbsolutePath());
						if (IwcmFsDB.useDBStorage(url))
						{
						fds = new FileDataSource(new File(IwcmFsDB.getTempFilePath(f.getPath())));
						}
						else
						{
							fds = new FileDataSource(new File(f.getAbsolutePath()));
						}

						at = new MimeBodyPart();
						at.setDataHandler(new DataHandler(fds));
					}
					else
					{
						//je to nejaky virtualny obrazok, je mozne ho asi len stiahnut
						url = Tools.replace(url, " ", "%20");

						//inline attachment
						String downloadUrl = url;
						if (url.indexOf("http")==-1)
						{
							downloadUrl = baseHref + url;
						}

						Logger.debug(SendMail.class,"ATT url: " + downloadUrl);

						downloadUrl = Tools.natUrl(downloadUrl);
						u = new URL(downloadUrl);

						uds = new URLDataSource(u);
						at = new MimeBodyPart();
						at.setDataHandler(new DataHandler(uds));
					}
				}

				//ponechame povodne URL obrazku
				boolean dmailDisableInlineImages = Constants.getBoolean("dmailDisableInlineImages");
				String trackGif = Constants.getString("dmailTrackopenGif");

				boolean whitelisted = false;
				String dmailWhitelistImageDomains = Constants.getString("dmailWhitelistImageDomains");
				if (Tools.isNotEmpty(dmailWhitelistImageDomains) && url.startsWith("http"))
				{
					try
					{
						String domain = url.substring(url.indexOf(":")+3, url.indexOf("/", 8));
						Logger.debug(SendMail.class, "Testing whitelist domain:"+domain);
						if (dmailWhitelistImageDomains.indexOf(domain)!=-1) whitelisted = true;
					}
					catch (Exception e)
					{
						sk.iway.iwcm.Logger.error(e);
					}
				}

				if(dmailDisableInlineImages || (Tools.isNotEmpty(trackGif) && url.indexOf(trackGif)!=-1) || whitelisted)
				{
					if (url.indexOf("http")==-1)
					{
						url = Tools.replace(url, "%20", " ");
						String newUrl = baseHref + url;
						rt = Tools.replace(rt, url, newUrl);
					}
				}
				else
				{
					String hash = putAsInline(rt, url);
					Logger.println(SendMail.class,"hash: " + hash);
					if (hash != null && at != null)
					{
						rt = Tools.replace(rt, url, "cid:WebJET." + hash);

						String onlyFile = onlyFile(url);
						if (onlyFile!=null && onlyFile.toLowerCase().endsWith(".png"))
						{
							//toto linux server niekedy nepozna
							at.setHeader("Content-Type", "image/png; name="+onlyFile);
						}

						at.setHeader("Content-ID", "<WebJET." + hash + ">");
						at.setHeader("Content-Disposition", "inline;\n filename=\""+onlyFile+"\"");
						related.addBodyPart(at);
						Logger.println(SendMail.class,"pridane");
					}
				}
			}
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		finally
		{

		}
		return rt;
	}

	/**
	 * Prida do emailu message part
	 * @param mp
	 * @param message
	 * @param related
	 * @throws MessagingException
	 */
	private static void putMessagePart(Multipart mp, String message, Multipart related) throws MessagingException
	{
		MimeMultipart content = new MimeMultipart("alternative");
		String txtVersion = isHtmlContent(message) ? sk.iway.Html2Text.html2text(message) : message;

		String emailEncoding = Constants.getString("emailEncoding");
		if (Tools.isEmpty(emailEncoding))
		{
			emailEncoding = SetCharacterEncodingFilter.getEncoding();
		}

		if (txtVersion != null && txtVersion.length() > 3)
		{
			MimeBodyPart text = new MimeBodyPart();

			txtVersion = Tools.replace(txtVersion, "&lt;", "<");
			txtVersion = Tools.replace(txtVersion, "&gt;", ">");

			//outlook maze nove riadky, fixuje sa to pridanim 2 medzier na kazdy novy riadok
			//http://www.masternewmedia.org/newsletter_publishing/newsletter_formatting/remove_line_breaks_issue_Microsoft_Outlook_2003_when_publishing_text_newsletters_20051217.htm
			txtVersion = Tools.replace(txtVersion, "\r", "");
			txtVersion = Tools.replace(txtVersion, "\n", "\n  ");
			if (txtVersion.startsWith("  ")==false) txtVersion = "  "+txtVersion;

			text.setText(txtVersion, emailEncoding);
			text.setHeader("MIME-Version", "1.0");
			text.setHeader("Content-Type", "text/plain; charset="+emailEncoding);
			//text.setContent(message, "text/plain; charset=windows-1250");
			//mp.addBodyPart(text);
			content.addBodyPart(text);
		}
		if (message != null) {
			String det = message.toLowerCase();
			if (isHtmlContent(det))
			{
				if (det.indexOf("<html")==-1)
				{
					message = "<html><body>" + message + "</body></html>";
				}

				if (related != null && related.getCount() > 0)
				{
					Multipart rela = new MimeMultipart("related");
					MimeBodyPart html = new MimeBodyPart();
					html.setContent(message, "text/html; charset="+emailEncoding);
					html.setHeader("MIME-Version", "1.0");
					html.setHeader("Content-Type", "text/html; charset="+emailEncoding);
					rela.addBodyPart(html);
					for (int i = 0; i < related.getCount(); i++)
					{
						rela.addBodyPart(related.getBodyPart(i));
					}
					MimeBodyPart wrap = new MimeBodyPart();
					wrap.setContent(rela);
					content.addBodyPart(wrap);
				}
				else
				{
					MimeBodyPart html = new MimeBodyPart();
					html.setContent(message, "text/html; charset="+emailEncoding);
					html.setHeader("MIME-Version", "1.0");
					html.setHeader("Content-Type", "text/html; charset="+emailEncoding);
					//mp.addBodyPart(html);
					content.addBodyPart(html);
				}
			}
		}
		//mp.setContent(content);
		//mp.addBodyPart(content);
		MimeBodyPart wrap = new MimeBodyPart();
		wrap.setContent(content);
		// HERE'S THE KEY
		mp.addBodyPart(wrap);
	}

	/**
	 * Otestuje, ci zadany test je HTML kod
	 * @param html
	 * @return
	 */
	public static boolean isHtmlContent(String html)
	{
		if (Tools.isNotEmpty(html)) {
			String htmlLC = html.toLowerCase();
			if (htmlLC.indexOf("<br")!=-1) return true;
			if (htmlLC.indexOf("<div>")!=-1) return true;
			if (htmlLC.indexOf("</p>")!=-1) return true;
			if (htmlLC.indexOf("</h")!=-1) return true;
			if (htmlLC.indexOf("</a>")!=-1) return true;
			if (htmlLC.indexOf("<img ")!=-1) return true;
			if (htmlLC.indexOf("</b>")!=-1) return true;
			if (htmlLC.indexOf("</strong>")!=-1) return true;
			if (htmlLC.indexOf("</ul>")!=-1) return true;
			if (htmlLC.indexOf("</ol>")!=-1) return true;
			if (htmlLC.indexOf("</td>")!=-1) return true;
		}

		return false;
	}

	/**
	 * Vypocita HASH hodnotu pre inline prilohu
	 * @param message
	 * @param iurl
	 * @return
	 */
	private static String putAsInline(String message, String iurl)
	{
		String rt = null;
		if (message.indexOf(iurl) != -1)
		{
			rt = String.valueOf(iurl.hashCode());
			// nemozeme pouzit replace all pretoze "regular expression" je aktivne
			// aj
			// na X? a to moze byt sucast URL
			// message = replaceAll(message,iurl,"cid:"+rt);
		}
		return rt;
	}


	/**
	 * Vrati nazov suboru z cesty
	 * @param f
	 * @return
	 */
	private static String onlyFile(String f)
	{
		try
		{
			//odstran image?v=aaaa z URL
			int questionMark = f.indexOf("?");
			if (questionMark > 0)
			{
				f = f.substring(0, questionMark);
			}

			f = java.net.URLDecoder.decode(f, SetCharacterEncodingFilter.getEncoding());
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		f = f.replace('\\', '/');
		int pos = f.lastIndexOf('/');
		if (pos == -1)
		{
			return(DocTools.removeChars(f));
		}
		return(DocTools.removeChars(f.substring(pos + 1)));
	}


	/**
	 *  Description of the Method
	 *
	 *@param  serverPath            Description of the Parameter
	 *@param  fileName              Description of the Parameter
	 *@param  mp  Description of the Parameter
	 */
	public static void attFile(String serverPath, String fileName, Multipart mp)
	{
		if (serverPath.contains("dms")){
            //toto nemoze byt povolene, pretoze to nepojde na WJ kde nie su DMS triedy, treba prerobit na reflectin DmsNotify.attDmsFile(fileName,mp);
		}
		else
		{
			Logger.println(SendMail.class,"attaching: "+serverPath+" fileName="+fileName);
			try
			{
				IwcmFile file = IwcmFile.fromVirtualPath(serverPath);
				//try absolute path otherwise
				if (!file.exists()) file = new IwcmFile(serverPath);

				if (file.exists())
				{
					MimeBodyPart mbp2 = new MimeBodyPart();

					FileDataSource fds = null;

					if (IwcmFsDB.useDBStorage(IwcmFsDB.getVirtualPath(file.getAbsolutePath())))
					{
						IwcmFsDB.writeFileToDisk(new File(file.getAbsolutePath()),new File(IwcmFsDB.getTempFilePath(file.getAbsolutePath())));
						fds=new FileDataSource(IwcmFsDB.getTempFilePath(file.getAbsolutePath()));
					}
					else
					{
						fds=new FileDataSource(file.getAbsolutePath());
					}

					String mimeType = Constants.getServletContext().getMimeType(file.getName().toLowerCase());
					if (Tools.isEmpty(mimeType)) mimeType = "application/octet-stream";

					mbp2.setDataHandler(new DataHandler(fds));
					mbp2.setHeader("Content-Type", mimeType);
					String emailEncoding = Constants.getString("emailEncoding");
					if (Tools.isEmpty(emailEncoding))
						emailEncoding = SetCharacterEncodingFilter.getEncoding();
					mbp2.setFileName(MimeUtility.encodeText(Tools.isNotEmpty(fileName) ? fileName : file.getName(), emailEncoding, null));

					mp.addBodyPart(mbp2);

					Logger.println(SendMail.class,"done");
				}
			}
			catch (Exception ex)
			{
				sk.iway.iwcm.Logger.error(ex);
			}
		}

	}

	/**
	 * Vrati kodovanie pre email podla nastavenia servera, alebo ak nie je prazdne podla konfiguracnej premennej emailEncoding
	 * @return
	 */
	public String getEncoding()
	{
		String emailEncoding = Constants.getString("emailEncoding");
		if (Tools.isEmpty(emailEncoding))
		{
			emailEncoding = SetCharacterEncodingFilter.getEncoding();
		}
		return emailEncoding;
	}

	/**
	 * Vytvori absolutne cesty v zadanom HTML kode
	 * @param htmlCode - HTML kod
	 * @param basePath - absolutna adresa, bez koncoveho /, napr. http://www.iway.sk
	 * @return
	 */
	public static String createAbsolutePath(String htmlCode, String basePath)
	{
		if (basePath == null)
		{
			return(htmlCode);
		}

		//Logger.println(this,"replace: " + htmlCode);

		//ak je tam uz base path, tak ju zrus, inak tam bude 2x
		//htmlCode = Tools.replace(htmlCode, basePath, "");
		//htmlCode = Tools.replace(htmlCode, "url(images/", "url("+basePath+"/images/");
		//htmlCode = Tools.replace(htmlCode, "url(/images/", "url("+basePath+"/images/");
		//htmlCode = Tools.replace(htmlCode, "\"/images/", "\""+basePath+"/images/");
		//htmlCode = Tools.replace(htmlCode, "\"/files/", "\""+basePath+"/files/");
		//htmlCode = Tools.replace(htmlCode, "\"/css/", "\""+basePath+"/css/");
		//htmlCode = Tools.replace(htmlCode, "/showdoc.do", "\""+basePath+"/showdoc.do");
		htmlCode = Tools.replace(htmlCode, "href=\"/", "href=\"" + basePath + "/");
		htmlCode = Tools.replace(htmlCode, "href='/", "href='" + basePath + "/");
		htmlCode = Tools.replace(htmlCode, "action=\"/", "action=\"" + basePath + "/");
		htmlCode = Tools.replace(htmlCode, "action='/", "action='" + basePath + "/");

		//replace URL v texte
		htmlCode = Tools.replace(htmlCode, "target=\"_blank\">/", "target=\"_blank\">"+basePath+"/");

		//Logger.println(this,"-------replaced: " + htmlCode);

		return(htmlCode);
	}

	public static String createAbsolutePath(String htmlCode, HttpServletRequest request)
	{
		String basePath = Tools.getBaseHref(request);
		return(createAbsolutePath(htmlCode, basePath));
	}

	/**
	 * @param props
	 * @return
	 */
	public static Session getSession(Properties props)
	{
		if (Constants.getBoolean("useAmazonSES")==false) {
			String port = "25";
			if (Tools.isNotEmpty(Constants.getString("smtpPort")))
			{
				props.setProperty("mail.smtp.port", Constants.getString("smtpPort"));
				port=Constants.getString("smtpPort");
			}
			else
			{
				props.setProperty("mail.smtp.port", port);
			}

			int smtpConnectionTimeoutMillis = Constants.getInt("smtpConnectionTimeoutMillis");
			if(smtpConnectionTimeoutMillis > 0)
			{
				props.put("mail.smtp.connectiontimeout", smtpConnectionTimeoutMillis);
			}

			if (Constants.getBoolean("smtpUseSSL"))
			{
				props.put("mail.smtp.socketFactory.port", port);
				props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
				props.put("mail.smtp.ssl.checkserveridentity", true);
				props.put("mail.smtp.socketFactory.fallback", "false");
			}
			// office365 (ak je port 587, smtpUseTLS musi byt true)
			else if(Constants.getBoolean("smtpUseTLS"))
			{
				props.put("mail.smtp.starttls.enable", "true");
				String smtpTLSVersion = Constants.getString("smtpTLSVersion");
				if (Tools.isNotEmpty(smtpTLSVersion)) props.put("mail.smtp.ssl.protocols", smtpTLSVersion);
			}
		}

		Session ms;
		if (Tools.isNotEmpty(Constants.getString("smtpUser")) &&  Tools.isNotEmpty(Constants.getString("smtpPassword")))
		{
			final class WJAuthenticator extends javax.mail.Authenticator
			{
				private final PasswordAuthentication authentication;

				public WJAuthenticator()
				{

					String username = Constants.getString("smtpUser");
					String password = Constants.getString("smtpPassword");
					authentication = new PasswordAuthentication(username, password);
				}

				@Override
				protected PasswordAuthentication getPasswordAuthentication()
				{
					return authentication;
				}
			}
			props.setProperty("mail.smtp.auth", "true");

			//ziskanie getDefaultInstance nam na Exchange vracalo chybu INFO: java.lang.SecurityException: Access to default session denied
			ms = Session.getInstance(props, new WJAuthenticator());
		}
		else if (Constants.getBoolean("useAmazonSES")
					&&  Tools.isNotEmpty(Constants.getString("amazonAccessKey"))
					&&  Tools.isNotEmpty(Constants.getString("amazonSecretKey"))) {
					props.put("mail.aws.user", Constants.getString("amazonAccessKey"));
					props.put("mail.aws.password", Constants.getString("amazonSecretKey"));

					if(Tools.isNotEmpty(Constants.getString("amazonEmailServiceHost"))) props.put("mail.aws.host", Constants.getString("amazonEmailServiceHost"));

					ms = Session.getDefaultInstance(props,null);
		} else {
			ms = Session.getDefaultInstance(props,null);
		}
		return ms;
	}

	/**
	 * Ziska prvu email adresu zo stringu typu meno@domena.sk,ine@domena.sk (pre polia, ktore mozu mat len jeden email ako from a replyTo)
	 * @param email
	 * @return
	 */
	public static String getFirstEmailAddress(String email) {
		int i = email.indexOf(',');
		if (i!=-1)
		{
			email = email.substring(0, i);
		}
		return email;
	}

	/**
	 * Return senderName of various email from config value moduleDefaultSenderName or defaultSenderName if module version is not defined.
	 * If config value is not defined, fallback is empty then domain name will be used.
	 * @param module
	 * @param fallbackName - if no defaultSenderName constants is defined this value will be used
	 * @return
	 */
	public static String getDefaultSenderName(String module, String fallbackName) {
		String senderName = Constants.getStringExecuteMacro(module + "DefaultSenderName");
		if (Tools.isEmpty(senderName)) senderName = Constants.getStringExecuteMacro("defaultSenderName");
		if (Tools.isEmpty(senderName)) {
			senderName = fallbackName;
		}
		if (Tools.isEmpty(senderName)) {
			RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
			if (rb != null) {
				senderName = rb.getDomain().replace("https://", "").replace("http://", "").replace("www.", "");
			}
		}
		return senderName;
	}

	/**
	 * Return senderEmail of various email from config value moduleDefaultSenderEmail or defaultSenderEmail if module version is not defined.
	 * If config value is not defined, fallback is empty then domain name will be used.
	 * @param module
	 * @param fallbackEmail - if no defaultSenderEmail constants is defined this value will be used
	 * @return
	 */
	public static String getDefaultSenderEmail(String module, String fallbackEmail) {
		String senderEmail = Constants.getStringExecuteMacro(module + "DefaultSenderEmail");
		if (Tools.isEmpty(senderEmail)) senderEmail = Constants.getStringExecuteMacro("defaultSenderEmail");
		if (Tools.isEmpty(senderEmail)) {
			senderEmail = fallbackEmail;
		}
		if (Tools.isEmpty(senderEmail)) {
			RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
			if (rb != null) {
				senderEmail = "no-reply@"+rb.getDomain().replace("https://", "").replace("http://", "").replace("www.", "");
			}
		}
		return senderEmail;
	}
}