Java tutorial
/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.core.util.mail.manager; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.zip.Adler32; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.Address; import javax.mail.Authenticator; import javax.mail.BodyPart; import javax.mail.Message.RecipientType; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.persistence.TemporalType; import javax.persistence.TypedQuery; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.RuntimeConstants; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.PersistentObject; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.commons.services.notifications.Publisher; import org.olat.core.commons.services.notifications.PublisherData; import org.olat.core.commons.services.notifications.Subscriber; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.id.UserConstants; import org.olat.core.manager.BasicManager; import org.olat.core.util.Encoder; import org.olat.core.util.StringHelper; import org.olat.core.util.WebappHelper; import org.olat.core.util.filter.impl.NekoHTMLFilter; import org.olat.core.util.mail.ContactList; import org.olat.core.util.mail.MailAttachment; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailContent; import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailHelper; import org.olat.core.util.mail.MailManager; import org.olat.core.util.mail.MailModule; import org.olat.core.util.mail.MailTemplate; import org.olat.core.util.mail.MailerResult; import org.olat.core.util.mail.MailerSMTPAuthenticator; import org.olat.core.util.mail.model.DBMail; import org.olat.core.util.mail.model.DBMailAttachment; import org.olat.core.util.mail.model.DBMailImpl; import org.olat.core.util.mail.model.DBMailLight; import org.olat.core.util.mail.model.DBMailLightImpl; import org.olat.core.util.mail.model.DBMailRecipient; import org.olat.core.util.vfs.FileStorage; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; /** * * Description:<br> * Manager which send e-mails, make the triage between mails which are * really send by POP, or only saved in the intern mail system (a.k.a on * the database). * * <P> * Initial Date: 24 mars 2011 <br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public class MailManagerImpl extends BasicManager implements MailManager { public static final String MAIL_TEMPLATE_FOLDER = "/customizing/mail/"; private VelocityEngine velocityEngine; private final MailModule mailModule; private DB dbInstance; private FileStorage attachmentStorage; private NotificationsManager notificationsManager; public MailManagerImpl(MailModule mailModule) { this.mailModule = mailModule; } /** * [used by Spring] * * @param dbInstance */ public void setDbInstance(DB dbInstance) { this.dbInstance = dbInstance; } /** * [used by Spring] * @param notificationsManager */ public void setNotificationsManager(NotificationsManager notificationsManager) { this.notificationsManager = notificationsManager; } /** * [used by Spring] */ public void init() { VFSContainer root = mailModule.getRootForAttachments(); attachmentStorage = new FileStorage(root); PublisherData pdata = getPublisherData(); SubscriptionContext scontext = getSubscriptionContext(); notificationsManager.getOrCreatePublisher(scontext, pdata); Properties p = null; try { velocityEngine = new VelocityEngine(); p = new Properties(); p.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.SimpleLog4JLogSystem"); p.setProperty(RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS, "org.olat.core.gui.render.velocity.InfinispanResourceCache"); p.setProperty("runtime.log.logsystem.log4j.category", "syslog"); velocityEngine.init(p); } catch (Exception e) { throw new RuntimeException("config error " + p.toString()); } } @Override public SubscriptionContext getSubscriptionContext() { return new SubscriptionContext("Inbox", 0l, ""); } @Override public PublisherData getPublisherData() { String data = ""; String businessPath = "[Inbox:0]"; PublisherData publisherData = new PublisherData("Inbox", data, businessPath); return publisherData; } @Override public Subscriber getSubscriber(Identity identity) { SubscriptionContext context = getSubscriptionContext(); if (context == null) return null; Publisher publisher = notificationsManager.getPublisher(context); if (publisher == null) { return null; } return notificationsManager.getSubscriber(identity, publisher); } @Override public void subscribe(Identity identity) { PublisherData data = getPublisherData(); SubscriptionContext context = getSubscriptionContext(); if (context != null) { notificationsManager.subscribe(identity, context, data); } } @Override public DBMail getMessageByKey(Long key) { StringBuilder sb = new StringBuilder(); sb.append("select mail from ").append(DBMailImpl.class.getName()).append(" mail") .append(" left join fetch mail.recipients recipients").append(" where mail.key=:mailKey"); List<DBMail> mails = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), DBMail.class) .setParameter("mailKey", key).getResultList(); if (mails.isEmpty()) return null; return mails.get(0); } @Override public List<DBMailAttachment> getAttachments(DBMailLight mail) { StringBuilder sb = new StringBuilder(); sb.append("select attachment from ").append(DBMailAttachment.class.getName()).append(" attachment") .append(" inner join attachment.mail mail").append(" where mail.key=:mailKey"); return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), DBMailAttachment.class) .setParameter("mailKey", mail.getKey()).getResultList(); } private DBMailAttachment getAttachment(Long key) { StringBuilder sb = new StringBuilder(); sb.append("select attachment from ").append(DBMailAttachment.class.getName()).append(" attachment") .append(" where attachment.key=:attachmentKey"); List<DBMailAttachment> attachments = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), DBMailAttachment.class).setParameter("attachmentKey", key) .getResultList(); if (attachments.isEmpty()) { return null; } return attachments.get(0); } @Override public String saveAttachmentToStorage(String name, String mimetype, long checksum, long size, InputStream stream) { String hasSibling = getAttachmentSibling(name, mimetype, checksum, size); if (StringHelper.containsNonWhitespace(hasSibling)) { return hasSibling; } String uuid = Encoder.md5hash(name + checksum); String dir = attachmentStorage.generateDir(uuid, false); VFSContainer container = attachmentStorage.getContainer(dir); String uniqueName = VFSManager.similarButNonExistingName(container, name); VFSLeaf file = container.createChildLeaf(uniqueName); VFSManager.copyContent(stream, file); return dir + uniqueName; } private String getAttachmentSibling(String name, String mimetype, long checksum, long size) { StringBuilder sb = new StringBuilder(); sb.append("select attachment from ").append(DBMailAttachment.class.getName()).append(" attachment") .append(" where attachment.checksum=:checksum and attachment.size=:size and attachment.name=:name"); if (mimetype == null) { sb.append(" and attachment.mimetype is null"); } else { sb.append(" and attachment.mimetype=:mimetype"); } TypedQuery<DBMailAttachment> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), DBMailAttachment.class).setParameter("checksum", new Long(checksum)) .setParameter("size", new Long(size)).setParameter("name", name); if (mimetype != null) { query.setParameter("mimetype", mimetype); } List<DBMailAttachment> attachments = query.getResultList(); if (attachments.isEmpty()) { return null; } return attachments.get(0).getPath(); } private int countAttachment(String path) { StringBuilder sb = new StringBuilder(); sb.append("select count(attachment) from ").append(DBMailAttachment.class.getName()).append(" attachment") .append(" where attachment.path=:path"); return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class) .setParameter("path", path).getSingleResult().intValue(); } @Override public VFSLeaf getAttachmentDatas(Long key) { DBMailAttachment attachment = getAttachment(key); return getAttachmentDatas(attachment); } @Override public VFSLeaf getAttachmentDatas(MailAttachment attachment) { String path = attachment.getPath(); if (StringHelper.containsNonWhitespace(path)) { VFSContainer root = mailModule.getRootForAttachments(); VFSItem item = root.resolve(path); if (item instanceof VFSLeaf) { return (VFSLeaf) item; } } return null; } @Override public boolean hasNewMail(Identity identity) { StringBuilder sb = new StringBuilder(); sb.append("select count(mail) from ").append(DBMailImpl.class.getName()).append(" mail") .append(" inner join mail.recipients recipient") .append(" inner join recipient.recipient recipientIdentity") .append(" where recipientIdentity.key=:recipientKey and recipient.read=false and recipient.deleted=false"); Number count = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class) .setParameter("recipientKey", identity.getKey()).getSingleResult(); return count.intValue() > 0; } /** * * @param mail * @param read cannot be null * @param identity * @return true if the read flag has been changed */ @Override public boolean setRead(DBMailLight mail, Boolean read, Identity identity) { if (mail == null || read == null || identity == null) throw new NullPointerException(); boolean changed = false; for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient == null) continue; if (recipient.getRecipient() != null && recipient.getRecipient().equalsByPersistableKey(identity)) { if (!read.equals(recipient.getRead())) { recipient.setRead(read); dbInstance.updateObject(recipient); changed |= true; } } } return changed; } @Override public DBMailLight toggleRead(DBMailLight mail, Identity identity) { Boolean read = null; for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient == null) continue; if (recipient.getRecipient() != null && recipient.getRecipient().equalsByPersistableKey(identity)) { if (read == null) { read = recipient.getRead() == null ? Boolean.FALSE : recipient.getRead(); } recipient.setRead(read.booleanValue() ? Boolean.FALSE : Boolean.TRUE); dbInstance.updateObject(recipient); } } return mail; } /** * @param mail * @param marked cannot be null * @param identity * @return true if the marked flag has been changed */ @Override public boolean setMarked(DBMailLight mail, Boolean marked, Identity identity) { if (mail == null || marked == null || identity == null) throw new NullPointerException(); boolean changed = false; for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient == null) continue; if (recipient != null && recipient.getRecipient() != null && recipient.getRecipient().equalsByPersistableKey(identity)) { if (marked == null) { marked = Boolean.FALSE; } if (!marked.equals(recipient.getMarked())) { recipient.setMarked(marked.booleanValue()); dbInstance.updateObject(recipient); changed |= true; } } } return changed; } @Override public DBMailLight toggleMarked(DBMailLight mail, Identity identity) { Boolean marked = null; for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient == null) continue; if (recipient != null && recipient.getRecipient() != null && recipient.getRecipient().equalsByPersistableKey(identity)) { if (marked == null) { marked = recipient.getMarked() == null ? Boolean.FALSE : recipient.getMarked(); } recipient.setMarked(marked.booleanValue() ? Boolean.FALSE : Boolean.TRUE); dbInstance.updateObject(recipient); } } return mail; } /** * Set the mail as deleted for a user * @param mail * @param identity */ @Override public void delete(DBMailLight mail, Identity identity, boolean deleteMetaMail) { if (mail == null) return;//already deleted if (StringHelper.containsNonWhitespace(mail.getMetaId()) && deleteMetaMail) { List<DBMailLight> mails = getEmailsByMetaId(mail.getMetaId()); for (DBMailLight childMail : mails) { deleteMail(childMail, identity, false); } } else { deleteMail(mail, identity, false); } } private void deleteMail(DBMailLight mail, Identity identity, boolean forceRemoveRecipient) { boolean delete = true; List<DBMailRecipient> updates = new ArrayList<DBMailRecipient>(); if (mail.getFrom() != null && mail.getFrom().getRecipient() != null) { if (identity.equalsByPersistableKey(mail.getFrom().getRecipient())) { DBMailRecipient from = mail.getFrom(); from.setDeleted(Boolean.TRUE); if (forceRemoveRecipient) { from.setRecipient(null); } updates.add(from); } if (mail.getFrom().getDeleted() != null) { delete &= mail.getFrom().getDeleted().booleanValue(); } } for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient == null) continue; if (recipient.getRecipient() != null && recipient.getRecipient().equalsByPersistableKey(identity)) { recipient.setDeleted(Boolean.TRUE); if (forceRemoveRecipient) { recipient.setRecipient(null); } updates.add(recipient); } if (recipient.getDeleted() != null) { delete &= recipient.getDeleted().booleanValue(); } } if (delete) { Set<String> paths = new HashSet<String>(); //all marked as deleted -> delete the mail List<DBMailAttachment> attachments = getAttachments(mail); for (DBMailAttachment attachment : attachments) { mail = attachment.getMail();//reload from the hibernate session dbInstance.deleteObject(attachment); if (StringHelper.containsNonWhitespace(attachment.getPath())) { paths.add(attachment.getPath()); } } dbInstance.deleteObject(mail); //try to remove orphans file for (String path : paths) { int count = countAttachment(path); if (count == 0) { VFSItem item = mailModule.getRootForAttachments().resolve(path); if (item instanceof VFSLeaf) { ((VFSLeaf) item).delete(); } } } } else { for (DBMailRecipient update : updates) { dbInstance.updateObject(update); } } } /** * Load all mails with the identity as from, mail which are not deleted * for this user. Recipients are loaded. * @param from * @param firstResult * @param maxResults * @return */ @Override public List<DBMailLight> getOutbox(Identity from, int firstResult, int maxResults, boolean fetchRecipients) { StringBuilder sb = new StringBuilder(); sb.append("select distinct(mail) from ").append(DBMailLightImpl.class.getName()).append(" mail") .append(" inner join fetch mail.from fromRecipient") .append(" inner join fromRecipient.recipient fromRecipientIdentity").append(" inner join ") .append(fetchRecipients ? "fetch" : "").append(" mail.recipients recipient").append(" inner join ") .append(fetchRecipients ? "fetch" : "").append(" recipient.recipient recipientIdentity") .append(" where fromRecipientIdentity.key=:fromKey and fromRecipient.deleted=false and recipientIdentity.key!=:fromKey") .append(" order by mail.creationDate desc"); TypedQuery<DBMailLight> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), DBMailLight.class).setParameter("fromKey", from.getKey()); if (maxResults > 0) { query.setMaxResults(maxResults); } if (firstResult > 0) { query.setFirstResult(firstResult); } List<DBMailLight> mails = query.getResultList(); return mails; } @Override public List<DBMailLight> getEmailsByMetaId(String metaId) { if (!StringHelper.containsNonWhitespace(metaId)) return Collections.emptyList(); StringBuilder sb = new StringBuilder(); sb.append("select mail from ").append(DBMailLightImpl.class.getName()).append(" mail") .append(" inner join fetch mail.from fromRecipient") .append(" inner join fromRecipient.recipient fromRecipientIdentity") .append(" where mail.metaId=:metaId"); return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), DBMailLight.class) .setParameter("metaId", metaId).getResultList(); } /** * Load all mails with the identity as recipient, only mails which are not deleted * for this user. Recipients are NOT loaded if not explicitly wanted! * @param identity * @param unreadOnly * @param fetchRecipients * @param from * @param firstResult * @param maxResults * @return */ @Override public List<DBMailLight> getInbox(Identity identity, Boolean unreadOnly, Boolean fetchRecipients, Date from, int firstResult, int maxResults) { StringBuilder sb = new StringBuilder(); String fetchOption = (fetchRecipients != null && fetchRecipients.booleanValue()) ? "fetch" : ""; sb.append("select mail from ").append(DBMailLightImpl.class.getName()).append(" mail") .append(" inner join fetch ").append(" mail.from fromRecipient").append(" inner join ") .append(fetchOption).append(" mail.recipients recipient").append(" inner join ").append(fetchOption) .append(" recipient.recipient recipientIdentity") .append(" where recipientIdentity.key=:recipientKey and recipient.deleted=false"); if (unreadOnly != null && unreadOnly.booleanValue()) { sb.append(" and recipient.read=false"); } if (from != null) { sb.append(" and mail.creationDate>=:from"); } sb.append(" order by mail.creationDate desc"); TypedQuery<DBMailLight> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), DBMailLight.class).setParameter("recipientKey", identity.getKey()); if (maxResults > 0) { query.setMaxResults(maxResults); } if (firstResult > 0) { query.setFirstResult(firstResult); } if (from != null) { query.setParameter("from", from, TemporalType.TIMESTAMP); } List<DBMailLight> mails = query.getResultList(); return mails; } @Override public String getMailTemplate() { File baseFolder = new File(WebappHelper.getUserDataRoot(), MAIL_TEMPLATE_FOLDER); File template = new File(baseFolder, "mail_template.html"); if (template.exists()) { try (InputStream in = new FileInputStream(template)) { return IOUtils.toString(in); } catch (IOException e) { logError("", e); } } return getDefaultMailTemplate(); } @Override public void setMailTemplate(String template) { File baseFolder = new File(WebappHelper.getUserDataRoot(), MAIL_TEMPLATE_FOLDER); if (!baseFolder.exists()) { baseFolder.mkdirs(); } OutputStream out = null; try { File templateFile = new File(baseFolder, "mail_template.html"); StringReader reader = new StringReader(template); out = new FileOutputStream(templateFile); IOUtils.copy(reader, out); } catch (IOException e) { logError("", e); } finally { IOUtils.closeQuietly(out); } } @Override public String getDefaultMailTemplate() { try (InputStream in = MailModule.class.getResourceAsStream("_content/mail_template.html")) { return IOUtils.toString(in); } catch (IOException e) { logError("Cannot read the default mail template", e); return null; } } @Override public MailBundle[] makeMailBundles(MailContext ctxt, List<Identity> recipientsTO, MailTemplate template, Identity sender, String metaId, MailerResult result) { List<MailBundle> bundles = new ArrayList<MailBundle>(); if (recipientsTO != null) { for (Identity recipient : recipientsTO) { MailBundle bundle = makeMailBundle(ctxt, recipient, template, sender, metaId, result); if (bundle != null) { bundles.add(bundle); } } } return bundles.toArray(new MailBundle[bundles.size()]); } @Override public MailBundle makeMailBundle(MailContext ctxt, Identity recipientTO, MailTemplate template, Identity sender, String metaId, MailerResult result) { MailBundle bundle; if (MailHelper.isDisabledMailAddress(recipientTO, result)) { bundle = null;//email disabled, nothing to do } else { MailContent msg = createWithContext(recipientTO, template, result); if (msg != null && result.getReturnCode() == MailerResult.OK) { // send mail bundle = new MailBundle(); bundle.setContext(ctxt); bundle.setFromId(sender); bundle.setToId(recipientTO); bundle.setMetaId(metaId); bundle.setContent(msg); } else { bundle = null; } } return bundle; } @Override public MailerResult sendMessage(MailBundle... bundles) { MailerResult result = new MailerResult(); for (MailBundle bundle : bundles) { MailContent content = decorateMail(bundle); if (mailModule.isInternSystem()) { saveDBMessage(bundle.getContext(), bundle.getFromId(), bundle.getFrom(), bundle.getToId(), bundle.getTo(), bundle.getCc(), bundle.getContactLists(), bundle.getMetaId(), content, result); } else { sendExternMessage(bundle.getFromId(), bundle.getFrom(), bundle.getToId(), bundle.getTo(), bundle.getCc(), bundle.getContactLists(), content, result); } } return result; } protected MailContent decorateMail(MailBundle bundle) { MailContent content = bundle.getContent(); String template = getMailTemplate(); boolean htmlTemplate = StringHelper.isHtml(template); String body = content.getBody(); boolean htmlContent = StringHelper.isHtml(body); if (htmlTemplate && !htmlContent) { body = body.replace("&", "&"); body = body.replace("<", "<"); body = body.replace("\n", "<br />"); } VelocityContext context = new VelocityContext(); context.put("content", body); context.put("footer", MailHelper.getMailFooter(bundle)); context.put("server", Settings.getServerContextPathURI()); StringWriter writer = new StringWriter(2000); MailerResult result = new MailerResult(); evaluate(context, template, writer, result); String decoratedBody; if (result.isSuccessful()) { decoratedBody = writer.toString(); } else { decoratedBody = content.getBody(); } return new MessageContent(content.getSubject(), decoratedBody, content.getAttachments()); } protected MailContent createWithContext(Identity recipient, MailTemplate template, MailerResult result) { VelocityContext context; if (template != null && template.getContext() != null) { context = new VelocityContext(template.getContext()); } else { context = new VelocityContext(); } template.putVariablesInMailContext(context, recipient); // merge subject template with context variables StringWriter subjectWriter = new StringWriter(); evaluate(context, template.getSubjectTemplate(), subjectWriter, result); // merge body template with context variables StringWriter bodyWriter = new StringWriter(); evaluate(context, template.getBodyTemplate(), bodyWriter, result); // check for errors - exit if (result.getReturnCode() != MailerResult.OK) { return null; } String subject = subjectWriter.toString(); String body = bodyWriter.toString(); List<File> checkedFiles = MailHelper.checkAttachments(template.getAttachments(), result); File[] attachments = checkedFiles.toArray(new File[checkedFiles.size()]); return new MessageContent(subject, body, attachments); } /** * Internal Helper: merges a velocity context with a template. * * @param context * @param template * @param writer writer that contains merged result * @param mailerResult */ protected void evaluate(Context context, String template, StringWriter writer, MailerResult mailerResult) { try { boolean result = velocityEngine.evaluate(context, writer, "mailTemplate", template); if (result) { mailerResult.setReturnCode(MailerResult.OK); } else { logWarn("can't send email from user template with no reason", null); mailerResult.setReturnCode(MailerResult.TEMPLATE_GENERAL_ERROR); } } catch (ParseErrorException e) { logWarn("can't send email from user template", e); mailerResult.setReturnCode(MailerResult.TEMPLATE_PARSE_ERROR); } catch (MethodInvocationException e) { logWarn("can't send email from user template", e); mailerResult.setReturnCode(MailerResult.TEMPLATE_GENERAL_ERROR); } catch (ResourceNotFoundException e) { logWarn("can't send email from user template", e); mailerResult.setReturnCode(MailerResult.TEMPLATE_GENERAL_ERROR); } catch (Exception e) { logWarn("can't send email from user template", e); mailerResult.setReturnCode(MailerResult.TEMPLATE_GENERAL_ERROR); } } private static class MessageContent implements MailContent { private final String subject; private final String body; private final List<File> attachments; public MessageContent(String subject, String body, File[] attachmentArr) { this.subject = subject; this.body = body; attachments = new ArrayList<File>(); if (attachmentArr != null && attachmentArr.length > 0) { for (File attachment : attachmentArr) { if (attachment != null && attachment.exists()) { attachments.add(attachment); } } } } public MessageContent(String subject, String body, List<File> attachmentList) { this.subject = subject; this.body = body; if (attachmentList == null) { this.attachments = new ArrayList<File>(1); } else { this.attachments = new ArrayList<File>(attachmentList); } } @Override public String getSubject() { return subject; } @Override public String getBody() { return body; } @Override public List<File> getAttachments() { return attachments; } } @Override public MailerResult forwardToRealInbox(Identity identity, DBMail mail, MailerResult result) { if (result == null) { result = new MailerResult(); } List<DBMailAttachment> attachments = getAttachments(mail); try { Address from = createAddress(WebappHelper.getMailConfig("mailFrom")); Address to = createAddress(identity, result, true); MimeMessage message = createForwardMimeMessage(from, to, mail.getSubject(), mail.getBody(), attachments, result); if (message != null) { sendMessage(message, result); } } catch (AddressException e) { logError("mailFrom is not configured", e); } return result; } @Override public MailerResult sendExternMessage(MailBundle bundle, MailerResult result) { return sendExternMessage(bundle.getFromId(), bundle.getFrom(), bundle.getToId(), bundle.getTo(), bundle.getCc(), bundle.getContactLists(), bundle.getContent(), result); } /** * Send the message via e-mail, always. * @param from * @param to * @param cc * @param contactLists * @param listAsBcc * @param subject * @param body * @param attachments * @return */ private MailerResult sendExternMessage(Identity fromId, String from, Identity toId, String to, Identity cc, List<ContactList> bccLists, MailContent content, MailerResult result) { if (result == null) { result = new MailerResult(); } MimeMessage mail = createMimeMessage(fromId, from, toId, to, cc, bccLists, content, result); if (mail != null) { sendMessage(mail, result); } return result; } private boolean wantRealMailToo(Identity id) { if (id == null) return false; String want = id.getUser().getPreferences().getReceiveRealMail(); if (want != null) { return "true".equals(want); } return mailModule.isReceiveRealMailUserDefaultSetting(); } protected DBMail saveDBMessage(MailContext context, Identity fromId, String from, Identity toId, String to, Identity cc, List<ContactList> bccLists, String metaId, MailContent content, MailerResult result) { try { DBMailImpl mail = new DBMailImpl(); if (result == null) { result = new MailerResult(); } boolean makeRealMail = makeRealMail(toId, cc, bccLists); Address fromAddress = null; List<Address> toAddress = new ArrayList<Address>(); List<Address> ccAddress = new ArrayList<Address>(); List<Address> bccAddress = new ArrayList<Address>(); if (fromId != null) { DBMailRecipient fromRecipient = new DBMailRecipient(); fromRecipient.setRecipient(fromId); if (StringHelper.containsNonWhitespace(from)) { fromRecipient.setEmailAddress(from); fromAddress = createFromAddress(from, result); } else { fromAddress = createFromAddress(fromId, result); } fromRecipient.setVisible(Boolean.TRUE); fromRecipient.setMarked(Boolean.FALSE); fromRecipient.setDeleted(Boolean.FALSE); mail.setFrom(fromRecipient); } else { if (!StringHelper.containsNonWhitespace(from)) { from = WebappHelper.getMailConfig("mailFrom"); } DBMailRecipient fromRecipient = new DBMailRecipient(); fromRecipient.setEmailAddress(from); fromRecipient.setVisible(Boolean.TRUE); fromRecipient.setMarked(Boolean.FALSE); fromRecipient.setDeleted(Boolean.TRUE);//marked as delted as nobody can read it mail.setFrom(fromRecipient); fromAddress = createFromAddress(from, result); } if (result.getReturnCode() != MailerResult.OK) { return null; } mail.setMetaId(metaId); String subject = content.getSubject(); if (subject != null && subject.length() > 500) { logWarn("Cut a too long subkect in name. Size: " + subject.length(), null); subject = subject.substring(0, 500); } mail.setSubject(subject); String body = content.getBody(); if (body != null && body.length() > 16777210) { logWarn("Cut a too long body in mail. Size: " + body.length(), null); body = body.substring(0, 16000000); } mail.setBody(body); mail.setLastModified(new Date()); if (context != null) { OLATResourceable ores = context.getOLATResourceable(); if (ores != null) { String resName = ores.getResourceableTypeName(); if (resName != null && resName.length() > 50) { logWarn("Cut a too long resourceable type name in mail context: " + resName, null); resName = resName.substring(0, 49); } mail.getContext().setResName(ores.getResourceableTypeName()); mail.getContext().setResId(ores.getResourceableId()); } String resSubPath = context.getResSubPath(); if (resSubPath != null && resSubPath.length() > 2000) { logWarn("Cut a too long resSubPath in mail context: " + resSubPath, null); resSubPath = resSubPath.substring(0, 2000); } mail.getContext().setResSubPath(resSubPath); String businessPath = context.getBusinessPath(); if (businessPath != null && businessPath.length() > 2000) { logWarn("Cut a too long resSubPath in mail context: " + businessPath, null); businessPath = businessPath.substring(0, 2000); } mail.getContext().setBusinessPath(businessPath); } //add to DBMailRecipient recipientTo = null; if (toId != null) { recipientTo = new DBMailRecipient(); if (toId instanceof PersistentObject) { recipientTo.setRecipient(toId); } else { to = toId.getUser().getProperty(UserConstants.EMAIL, null); } if (StringHelper.containsNonWhitespace(to)) { recipientTo.setEmailAddress(to); } recipientTo.setVisible(Boolean.TRUE); recipientTo.setDeleted(Boolean.FALSE); recipientTo.setMarked(Boolean.FALSE); recipientTo.setRead(Boolean.FALSE); } else if (StringHelper.containsNonWhitespace(to)) { recipientTo = new DBMailRecipient(); recipientTo.setEmailAddress(to); recipientTo.setVisible(Boolean.TRUE); recipientTo.setDeleted(Boolean.TRUE); recipientTo.setMarked(Boolean.FALSE); recipientTo.setRead(Boolean.FALSE); } if (recipientTo != null) { mail.getRecipients().add(recipientTo); createAddress(toAddress, recipientTo, true, result, true); } if (makeRealMail && StringHelper.containsNonWhitespace(to)) { createAddress(toAddress, to); } if (cc != null) { DBMailRecipient recipient = new DBMailRecipient(); if (cc instanceof PersistentObject) { recipient.setRecipient(cc); } else { recipient.setEmailAddress(cc.getUser().getProperty(UserConstants.EMAIL, null)); } recipient.setVisible(Boolean.TRUE); recipient.setDeleted(Boolean.FALSE); recipient.setMarked(Boolean.FALSE); recipient.setRead(Boolean.FALSE); mail.getRecipients().add(recipient); createAddress(ccAddress, recipient, false, result, true); } //add bcc recipients appendRecipients(mail, bccLists, toAddress, bccAddress, false, makeRealMail, result); dbInstance.getCurrentEntityManager().persist(mail); //save attachments List<File> attachments = content.getAttachments(); if (attachments != null && !attachments.isEmpty()) { for (File attachment : attachments) { FileInputStream in = null; try { DBMailAttachment data = new DBMailAttachment(); data.setSize(attachment.length()); data.setName(attachment.getName()); long checksum = FileUtils.checksum(attachment, new Adler32()).getValue(); data.setChecksum(new Long(checksum)); data.setMimetype(WebappHelper.getMimeType(attachment.getName())); in = new FileInputStream(attachment); String path = saveAttachmentToStorage(data.getName(), data.getMimetype(), checksum, attachment.length(), in); data.setPath(path); data.setMail(mail); dbInstance.getCurrentEntityManager().persist(data); } catch (FileNotFoundException e) { logError("File attachment not found: " + attachment, e); } catch (IOException e) { logError("Error with file attachment: " + attachment, e); } finally { IOUtils.closeQuietly(in); } } } if (makeRealMail) { //check that we send an email to someone if (!toAddress.isEmpty() || !ccAddress.isEmpty() || !bccAddress.isEmpty()) { sendRealMessage(fromAddress, toAddress, ccAddress, bccAddress, subject, body, attachments, result); } } //update subscription for (DBMailRecipient recipient : mail.getRecipients()) { if (recipient.getRecipient() != null) { subscribe(recipient.getRecipient()); } } SubscriptionContext subContext = getSubscriptionContext(); notificationsManager.markPublisherNews(subContext, null, false); return mail; } catch (AddressException e) { logError("Cannot send e-mail: ", e); result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); return null; } } private void appendRecipients(DBMailImpl mail, List<ContactList> ccLists, List<Address> toAddress, List<Address> ccAddress, boolean visible, boolean makeRealMail, MailerResult result) throws AddressException { //append cc/bcc recipients if (ccLists != null && !ccLists.isEmpty()) { for (ContactList contactList : ccLists) { if (makeRealMail && StringHelper.containsNonWhitespace(contactList.getName())) { Address[] groupAddress = InternetAddress.parse(contactList.getRFC2822Name() + ";"); if (groupAddress != null && groupAddress.length > 0) { for (Address groupAdd : groupAddress) { toAddress.add(groupAdd); } } } for (String email : contactList.getStringEmails().values()) { DBMailRecipient recipient = new DBMailRecipient(); recipient.setEmailAddress(email); recipient.setGroup(contactList.getName()); recipient.setVisible(visible); recipient.setDeleted(Boolean.FALSE); recipient.setMarked(Boolean.FALSE); recipient.setRead(Boolean.FALSE); mail.getRecipients().add(recipient); if (makeRealMail) { createAddress(ccAddress, recipient, false, result, false); } } for (Identity identityEmail : contactList.getIdentiEmails().values()) { DBMailRecipient recipient = new DBMailRecipient(); if (identityEmail instanceof PersistentObject) { recipient.setRecipient(identityEmail); } else { recipient.setEmailAddress(identityEmail.getUser().getProperty(UserConstants.EMAIL, null)); } recipient.setGroup(contactList.getName()); recipient.setVisible(visible); recipient.setDeleted(Boolean.FALSE); recipient.setMarked(Boolean.FALSE); recipient.setRead(Boolean.FALSE); mail.getRecipients().add(recipient); if (makeRealMail) { createAddress(ccAddress, recipient, false, result, false); } } } } } private boolean makeRealMail(Identity toId, Identity cc, List<ContactList> bccLists) { //need real mail to??? boolean makeRealMail = false; // can occur on self-registration if (toId == null && cc == null && bccLists == null) return true; if (toId != null) { makeRealMail |= wantRealMailToo(toId); } if (cc != null) { makeRealMail |= wantRealMailToo(cc); } //add bcc recipients if (bccLists != null && !bccLists.isEmpty()) { for (ContactList contactList : bccLists) { for (Identity identityEmail : contactList.getIdentiEmails().values()) { makeRealMail |= wantRealMailToo(identityEmail); } if (!contactList.getStringEmails().isEmpty()) { makeRealMail |= true; } } } return makeRealMail; } private MimeMessage createMimeMessage(Identity fromId, String mailFrom, Identity toId, String to, Identity ccId, List<ContactList> bccLists, MailContent content, MailerResult result) { try { Address from; if (StringHelper.containsNonWhitespace(mailFrom)) { from = createFromAddress(mailFrom, result); } else if (fromId != null) { from = createFromAddress(fromId, result); } else { // fxdiff: change from/replyto, see FXOLAT-74 . if no from is set, use default sysadmin-address (adminemail). from = createAddress(WebappHelper.getMailConfig("mailReplyTo")); if (from == null) { logError("MailConfigError: mailReplyTo is not set", null); } } List<Address> toList = new ArrayList<Address>(); if (StringHelper.containsNonWhitespace(to)) { Address[] toAddresses = InternetAddress.parse(to); for (Address toAddress : toAddresses) { toList.add(toAddress); } } else if (toId != null) { Address toAddress = createAddress(toId, result, true); if (toAddress != null) { toList.add(toAddress); } } List<Address> ccList = new ArrayList<Address>(); if (ccId != null) { Address ccAddress = createAddress(ccId, result, true); if (ccAddress != null) { ccList.add(ccAddress); } } //add bcc contact lists List<Address> bccList = new ArrayList<Address>(); if (bccLists != null) { for (ContactList contactList : bccLists) { if (StringHelper.containsNonWhitespace(contactList.getName())) { Address[] groupNames = InternetAddress.parse(contactList.getRFC2822Name() + ";"); for (Address groupName : groupNames) { toList.add(groupName); } } Address[] members = contactList.getEmailsAsAddresses(); for (Address member : members) { bccList.add(member); } } } Address[] tos = toList.toArray(new Address[toList.size()]); Address[] ccs = ccList.toArray(new Address[ccList.size()]); Address[] bccs = bccList.toArray(new Address[bccList.size()]); return createMimeMessage(from, tos, ccs, bccs, content.getSubject(), content.getBody(), content.getAttachments(), result); } catch (MessagingException e) { logError("", e); return null; } } private Address createAddress(String address) throws AddressException { if (address == null) return null; return new InternetAddress(address); } private Address createAddressWithName(String address, String name) throws UnsupportedEncodingException, AddressException { InternetAddress add = new InternetAddress(address, name); try { add.validate(); } catch (AddressException e) { throw e; } return add; } private Address createFromAddress(String address, MailerResult result) throws AddressException { try { Address add = new InternetAddress(address); return add; } catch (AddressException e) { result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); throw e; } } private boolean createAddress(List<Address> addressList, String address) throws AddressException { Address add = createAddress(address); if (add != null) { addressList.add(add); } return true; } private boolean createAddress(List<Address> addressList, DBMailRecipient recipient, boolean force, MailerResult result, boolean error) { String emailAddress = recipient.getEmailAddress(); if (recipient.getRecipient() == null) { try { Address address = createAddress(emailAddress); if (address != null) { addressList.add(address); return true; } else { if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } } catch (AddressException e) { if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } } else if (recipient.getRecipient().getStatus() == Identity.STATUS_LOGIN_DENIED) { result.addFailedIdentites(recipient.getRecipient()); } else { if (force || wantRealMailToo(recipient.getRecipient())) { if (!StringHelper.containsNonWhitespace(emailAddress)) { emailAddress = recipient.getRecipient().getUser().getProperty(UserConstants.EMAIL, null); } try { Address address = createAddress(emailAddress); if (address != null) { addressList.add(address); return true; } else { result.addFailedIdentites(recipient.getRecipient()); if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } } catch (AddressException e) { result.addFailedIdentites(recipient.getRecipient()); if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } } } return false; } private Address createAddress(Identity recipient, MailerResult result, boolean error) { if (recipient != null) { if (recipient.getStatus() == Identity.STATUS_LOGIN_DENIED) { result.addFailedIdentites(recipient); } else { String emailAddress = recipient.getUser().getProperty(UserConstants.EMAIL, null); Address address; try { address = createAddress(emailAddress); if (address == null) { result.addFailedIdentites(recipient); if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } return address; } catch (AddressException e) { result.addFailedIdentites(recipient); if (error) { result.setReturnCode(MailerResult.RECIPIENT_ADDRESS_ERROR); } } } } return null; } private Address createFromAddress(Identity recipient, MailerResult result) { if (recipient != null) { String emailAddress = recipient.getUser().getProperty(UserConstants.EMAIL, null); String name = recipient.getUser().getProperty(UserConstants.FIRSTNAME, null) + " " + recipient.getUser().getProperty(UserConstants.LASTNAME, null); Address address; try { address = createAddressWithName(emailAddress, name); if (address == null) { result.addFailedIdentites(recipient); result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); } return address; } catch (AddressException e) { result.addFailedIdentites(recipient); result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); } catch (UnsupportedEncodingException e) { result.addFailedIdentites(recipient); result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); } } return null; } private void sendRealMessage(Address from, List<Address> toList, List<Address> ccList, List<Address> bccList, String subject, String body, List<File> attachments, MailerResult result) { Address[] tos = null; if (toList != null && !toList.isEmpty()) { tos = new Address[toList.size()]; tos = toList.toArray(tos); } Address[] ccs = null; if (ccList != null && !ccList.isEmpty()) { ccs = new Address[ccList.size()]; ccs = ccList.toArray(ccs); } Address[] bccs = null; if (bccList != null && !bccList.isEmpty()) { bccs = new Address[bccList.size()]; bccs = bccList.toArray(bccs); } MimeMessage msg = createMimeMessage(from, tos, ccs, bccs, subject, body, attachments, result); sendMessage(msg, result); } private MimeMessage createForwardMimeMessage(Address from, Address to, String subject, String body, List<DBMailAttachment> attachments, MailerResult result) { try { Address convertedFrom = getRawEmailFromAddress(from); MimeMessage msg = createMessage(convertedFrom); msg.setFrom(from); msg.setSubject(subject, "utf-8"); if (to != null) { msg.addRecipient(RecipientType.TO, to); } if (attachments != null && !attachments.isEmpty()) { // with attachment use multipart message Multipart multipart = new MimeMultipart(); // 1) add body part BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setText(body); multipart.addBodyPart(messageBodyPart); // 2) add attachments for (DBMailAttachment attachment : attachments) { // abort if attachment does not exist if (attachment == null || attachment.getSize() <= 0) { result.setReturnCode(MailerResult.ATTACHMENT_INVALID); logError("Tried to send mail wit attachment that does not exist::" + (attachment == null ? null : attachment.getName()), null); return msg; } messageBodyPart = new MimeBodyPart(); VFSLeaf data = getAttachmentDatas(attachment); DataSource source = new VFSDataSource(attachment.getName(), attachment.getMimetype(), data); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(attachment.getName()); multipart.addBodyPart(messageBodyPart); } // Put parts in message msg.setContent(multipart); } else { // without attachment everything is easy, just set as text msg.setText(body, "utf-8"); } msg.setSentDate(new Date()); msg.saveChanges(); return msg; } catch (MessagingException e) { logError("", e); return null; } } /** * * @param bounceAdress must be a raw email, without anything else (no "bla bli <bla@bli.ch>" !) * @return */ private MimeMessage createMessage(Address bounceAdress) { String mailhost = WebappHelper.getMailConfig("mailhost"); String mailhostTimeout = WebappHelper.getMailConfig("mailTimeout"); boolean sslEnabled = Boolean.parseBoolean(WebappHelper.getMailConfig("sslEnabled")); boolean sslCheckCertificate = Boolean.parseBoolean(WebappHelper.getMailConfig("sslCheckCertificate")); Authenticator smtpAuth; if (WebappHelper.isMailHostAuthenticationEnabled()) { String smtpUser = WebappHelper.getMailConfig("smtpUser"); String smtpPwd = WebappHelper.getMailConfig("smtpPwd"); smtpAuth = new MailerSMTPAuthenticator(smtpUser, smtpPwd); } else { smtpAuth = null; } Properties p = new Properties(); p.put("mail.smtp.from", bounceAdress.toString()); p.put("mail.smtp.host", mailhost); p.put("mail.smtp.timeout", mailhostTimeout); p.put("mail.smtp.connectiontimeout", mailhostTimeout); p.put("mail.smtp.ssl.enable", sslEnabled); p.put("mail.smtp.ssl.checkserveridentity", sslCheckCertificate); Session mailSession; if (smtpAuth == null) { mailSession = javax.mail.Session.getInstance(p); } else { // use smtp authentication from configuration p.put("mail.smtp.auth", "true"); mailSession = Session.getDefaultInstance(p, smtpAuth); } if (isLogDebugEnabled()) { // enable mail session debugging on console mailSession.setDebug(true); } return new MimeMessage(mailSession); } // converts an address "bla bli <bla@bli.ch>" => "bla@bli.ch" private InternetAddress getRawEmailFromAddress(Address address) throws AddressException { if (address == null) { throw new AddressException("Address cannot be null"); } InternetAddress fromAddress = new InternetAddress(address.toString()); String fromPlainAddress = fromAddress.getAddress(); return new InternetAddress(fromPlainAddress); } @Override public MimeMessage createMimeMessage(Address from, Address[] tos, Address[] ccs, Address[] bccs, String subject, String body, List<File> attachments, MailerResult result) { try { // see FXOLAT-74: send all mails as <fromemail> (in config) to have a valid reverse lookup and therefore pass spam protection. // following doesn't work correctly, therefore add bounce-address in message already Address convertedFrom = getRawEmailFromAddress(from); MimeMessage msg = createMessage(convertedFrom); Address viewableFrom = createAddressWithName(WebappHelper.getMailConfig("mailFrom"), WebappHelper.getMailConfig("mailFromName")); msg.setFrom(viewableFrom); msg.setSubject(subject, "utf-8"); // reply to can only be an address without name (at least for postfix!), see FXOLAT-312 msg.setReplyTo(new Address[] { convertedFrom }); if (tos != null && tos.length > 0) { msg.addRecipients(RecipientType.TO, tos); } if (ccs != null && ccs.length > 0) { msg.addRecipients(RecipientType.CC, ccs); } if (bccs != null && bccs.length > 0) { msg.addRecipients(RecipientType.BCC, bccs); } if (attachments != null && !attachments.isEmpty()) { // with attachment use multipart message Multipart multipart = new MimeMultipart("mixed"); // 1) add body part if (StringHelper.isHtml(body)) { Multipart alternativePart = createMultipartAlternative(body); MimeBodyPart wrap = new MimeBodyPart(); wrap.setContent(alternativePart); multipart.addBodyPart(wrap); } else { BodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setText(body); multipart.addBodyPart(messageBodyPart); } // 2) add attachments for (File attachmentFile : attachments) { // abort if attachment does not exist if (attachmentFile == null || !attachmentFile.exists()) { result.setReturnCode(MailerResult.ATTACHMENT_INVALID); logError("Tried to send mail wit attachment that does not exist::" + (attachmentFile == null ? null : attachmentFile.getAbsolutePath()), null); return msg; } BodyPart filePart = new MimeBodyPart(); DataSource source = new FileDataSource(attachmentFile); filePart.setDataHandler(new DataHandler(source)); filePart.setFileName(attachmentFile.getName()); multipart.addBodyPart(filePart); } // Put parts in message msg.setContent(multipart); } else { // without attachment everything is easy, just set as text if (StringHelper.isHtml(body)) { msg.setContent(createMultipartAlternative(body)); } else { msg.setText(body, "utf-8"); } } msg.setSentDate(new Date()); msg.saveChanges(); return msg; } catch (AddressException e) { result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); logError("", e); return null; } catch (MessagingException e) { result.setReturnCode(MailerResult.SEND_GENERAL_ERROR); logError("", e); return null; } catch (UnsupportedEncodingException e) { result.setReturnCode(MailerResult.SENDER_ADDRESS_ERROR); logError("", e); return null; } } private Multipart createMultipartAlternative(String text) throws MessagingException { String pureText = new NekoHTMLFilter().filter(text, true); MimeBodyPart textPart = new MimeBodyPart(); textPart.setText(pureText, "utf-8"); MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setText(text, "utf-8", "html"); Multipart multipart = new MimeMultipart("alternative"); multipart.addBodyPart(textPart); multipart.addBodyPart(htmlPart); return multipart; } @Override public void sendMessage(MimeMessage msg, MailerResult result) { try { if (Settings.isJUnitTest()) { //we want not send really e-mails } else if (mailModule.isMailHostEnabled() && result.getReturnCode() == MailerResult.OK) { // now send the mail if (Settings.isDebuging()) { try { logInfo("E-mail send: " + msg.getSubject()); logInfo("Content : " + msg.getContent()); } catch (IOException e) { logError("", e); } } Transport.send(msg); } else if (Settings.isDebuging() && result.getReturnCode() == MailerResult.OK) { try { logInfo("E-mail send: " + msg.getSubject()); logInfo("Content : " + msg.getContent()); } catch (IOException e) { logError("", e); } } else { result.setReturnCode(MailerResult.MAILHOST_UNDEFINED); } } catch (MessagingException e) { result.setReturnCode(MailerResult.SEND_GENERAL_ERROR); logWarn("Could not send mail", e); } } private static class VFSDataSource implements DataSource { private final String name; private final String contentType; private final VFSLeaf file; public VFSDataSource(String name, String contentType, VFSLeaf file) { this.name = name; this.contentType = contentType; this.file = file; } @Override public String getContentType() { return contentType; } @Override public InputStream getInputStream() throws IOException { return file.getInputStream(); } @Override public String getName() { return name; } @Override public OutputStream getOutputStream() throws IOException { return null; } } }