package pl.net.bluesoft.rnd.pt.ext.emailcapture; import pl.net.bluesoft.rnd.processtool.ProcessToolContext; import pl.net.bluesoft.rnd.processtool.bpm.ProcessToolBpmSession; import pl.net.bluesoft.rnd.processtool.model.*; import pl.net.bluesoft.rnd.processtool.model.config.ProcessStateAction; import pl.net.bluesoft.rnd.pt.ext.emailcapture.model.EmailCheckerConfiguration; import pl.net.bluesoft.rnd.pt.ext.emailcapture.model.EmailCheckerRuleConfiguration; import pl.net.bluesoft.rnd.pt.utils.cmis.CmisAtomSessionFacade; import pl.net.bluesoft.util.lang.StringUtil; import javax.mail.*; import javax.mail.search.FlagTerm; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import static pl.net.bluesoft.util.lang.FormatUtil.nvl; import static pl.net.bluesoft.util.lang.StringUtil.hasText; public class EmailChecker { private static final Logger logger = Logger.getLogger(EmailChecker.class.getName()); private ProcessToolContext context; public EmailChecker(ProcessToolContext context) { this.context = context; } public void run() { List<EmailCheckerConfiguration> configs = context.getHibernateSession().createCriteria(EmailCheckerConfiguration.class).list(); for (EmailCheckerConfiguration cfg : configs) { try { execute(cfg); } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); } } } public void execute(EmailCheckerConfiguration cfg) throws Exception { ProcessToolBpmSession toolBpmSession = context.getProcessToolSessionFactory().createSession( new UserData(cfg.getAutomaticUser(), cfg.getAutomaticUser(), cfg.getAutomaticUser()), new HashSet()); ByteArrayInputStream bis = new ByteArrayInputStream(cfg.getMailSessionProperties().getBytes()); final Properties cfgProperties = new Properties(); cfgProperties.load(bis); final String protocol = cfgProperties.getProperty("mail.store.protocol"); Session session = Session.getInstance(cfgProperties, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(cfgProperties.getProperty("mail." + protocol + ".user"), cfgProperties.getProperty("mail." + protocol + ".password")); } }); Store store = session.getStore(); store.connect(); logger.info("Connected to mail service using " + cfg.getMailSessionProperties()); String searchDirectory = cfgProperties.getProperty("search.directory"); Folder folder = hasText(searchDirectory) ? store.getFolder(searchDirectory) : store.getFolder("inbox"); folder.open(Folder.READ_WRITE); logger.info("Folder " + searchDirectory + " opened successfully"); Message[] messages = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); logger.info("Found " + messages.length + " messages in " + folder.getFullName()); List<Message> processed = new ArrayList(); for (Message msg : messages) { try { processMessage(msg, cfg, toolBpmSession); processed.add(msg); } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); } } folder.setFlags(processed.toArray(new Message[processed.size()]), new Flags(Flags.Flag.SEEN), true); folder.close(false); store.close(); } private void processMessage(Message msg, EmailCheckerConfiguration cfg, ProcessToolBpmSession toolBpmSession) throws MessagingException, IOException { String subject = msg.getSubject(); StringBuilder recipientBuilder = new StringBuilder(); if (msg.getHeader("To") != null) { for (String h : msg.getHeader("To")) { recipientBuilder.append(h).append(","); } } if (msg.getHeader("Cc") != null) { for (String h : msg.getHeader("Cc")) { recipientBuilder.append(h).append(","); } } String recipients = recipientBuilder.toString(); StringBuilder senderBuilder = new StringBuilder(); if (msg.getFrom() != null) { for (Address a : msg.getFrom()) { senderBuilder.append(a.toString()).append(","); } } String sender = senderBuilder.toString(); String description = subject + ", from: " + recipients + ", sent by: " + sender; logger.fine("Processing message: " + description); for (EmailCheckerRuleConfiguration rule : cfg.getRules()) { logger.fine("Checking rule " + rule.getId() + " against message " + description); if (hasText(rule.getSubjectRegexp())) { if (subject == null || !subject.matches(rule.getSubjectRegexp())) { continue; } } String preparedSubject = subject.toUpperCase(); if (hasText(rule.getSubjectRemovables())) { for (String removable : rule.getSubjectRemovables().split("\\s")) { preparedSubject = preparedSubject.replace(removable.toUpperCase(), ""); } } preparedSubject = preparedSubject.replaceAll("[^A-Z0-9_\\-]*", ""); logger.fine("Prepared subject: " + preparedSubject); ProcessInstance existingPi = null; if (rule.isLookupRunningProcesses()) { List<ProcessInstance> instancesByExternalKey = context.getProcessInstanceDAO().findProcessInstancesByKeyword(preparedSubject, rule.getProcessCode()); for (ProcessInstance pi : instancesByExternalKey) { boolean running = toolBpmSession.isProcessRunning(pi.getInternalId(), context); logger.fine("Found existing process for " + preparedSubject + ", ID: " + pi.getInternalId()); if (running && pi.getDefinition().getBpmDefinitionKey().equals(rule.getProcessCode())) { logger.info("Found existing and RUNNING process for " + preparedSubject + ", ID: " + pi.getInternalId()); existingPi = pi; break; } } } if (existingPi == null) { if (hasText(rule.getRecipientRegexp())) { if (recipients == null || recipients.isEmpty() || !recipients.matches(rule.getRecipientRegexp())) { continue; } } if (hasText(rule.getSenderRegexp())) { if (sender == null || sender.isEmpty() || !sender.matches(rule.getSenderRegexp())) { continue; } } } logger.fine("Rule " + rule.getId() + " has matched message " + description + ", existing process: " + existingPi); if (existingPi == null && hasText(rule.getProcessIdSubjectLookupRegexp())) { Matcher m = java.util.regex.Pattern.compile(rule.getProcessIdSubjectLookupRegexp()).matcher(subject); if (m.matches()) { String processId = m.group(1); existingPi = nvl( context.getProcessInstanceDAO().getProcessInstanceByExternalId(processId), context.getProcessInstanceDAO().getProcessInstanceByInternalId(processId)); if (existingPi != null) { logger.fine("Found existing process for " + processId + ", ID: " + existingPi.getInternalId()); } } } if (existingPi == null && rule.getStartNewProcesses()) { logger.fine("Starting new process for rule " + rule.getId() + " on matched message " + description + ", process code: " + rule.getProcessCode()); existingPi = toolBpmSession.createProcessInstance(context.getProcessDefinitionDAO().getActiveConfigurationByKey(rule.getProcessCode()), null, context, subject, preparedSubject, "email", null); //save initial email data existingPi.addAttribute(new ProcessInstanceSimpleAttribute("email_from", sender)); existingPi.addAttribute(new ProcessInstanceSimpleAttribute("email_subject", msg.getSubject())); if (msg.getContent() instanceof Multipart) { Multipart multipart = (Multipart) msg.getContent(); for (int i = 0; i < multipart.getCount(); ++i) { BodyPart part = multipart.getBodyPart(i); if (part.getContentType() == null || part.getContentType().startsWith("text/")) { logger.info("Skipping multipart attachment #" + i); continue; } existingPi.addAttribute(new ProcessInstanceSimpleAttribute("email_body", new String(toByteArray(part.getInputStream())))); } } else { existingPi.addAttribute(new ProcessInstanceSimpleAttribute("email_body", new String(toByteArray(msg.getInputStream())))); } context.getProcessInstanceDAO().saveProcessInstance(existingPi); logger.info("Started new process for rule " + rule.getId() + " on matched message " + description + ", process code: " + rule.getProcessCode() + " new process id: " + existingPi.getInternalId()); } if (existingPi != null && hasText(rule.getRunningProcessActionName())) { Collection<BpmTask> taskList = toolBpmSession.findProcessTasks(existingPi, context); for (BpmTask t : taskList) { if (!hasText(rule.getProcessTaskName()) || rule.getProcessTaskName().equalsIgnoreCase(t.getTaskName())) { Set<ProcessStateAction> actions = context.getProcessDefinitionDAO().getProcessStateConfiguration(t).getActions(); for (ProcessStateAction a : actions) { if (rule.getRunningProcessActionName().equals(a.getBpmName())) { toolBpmSession.performAction(a, t, context); logger.info("Performed action " + rule.getId() + " on matched message " + description + ", process code: " + rule.getProcessCode() + " process id: " + existingPi.getInternalId()); break; } } } } } if (existingPi != null && hasText(rule.getRepositoryAtomUrl())) { logger.fine("Uploading CMIS documents, process ID: " + existingPi.getInternalId()); CmisAtomSessionFacade sessionFacade = new CmisAtomSessionFacade(rule.getRepositoryUser(), rule.getRepositoryPassword(), rule.getRepositoryAtomUrl(), rule.getRepositoryId()); String folderId = null; for (ProcessInstanceAttribute at : existingPi.getProcessAttributes()) { if (at instanceof ProcessInstanceSimpleAttribute) { ProcessInstanceSimpleAttribute pisa = (ProcessInstanceSimpleAttribute) at; if (pisa.getKey().equals(rule.getFolderAttributeName())) { folderId = pisa.getValue(); break; } } } org.apache.chemistry.opencmis.client.api.Folder mainFolder; if (folderId == null) { mainFolder = sessionFacade.createFolderIfNecessary(nvl(rule.getNewFolderPrefix(), "") + existingPi.getInternalId(), rule.getRootFolderPath()); if (StringUtil.hasText(rule.getSubFolder())) mainFolder = sessionFacade.createFolderIfNecessary(rule.getSubFolder(), mainFolder.getPath()); folderId = mainFolder.getId(); ProcessInstanceSimpleAttribute attr; attr = new ProcessInstanceSimpleAttribute(); attr.setKey("emailSender"); attr.setValue(sender); existingPi.addAttribute(attr); attr = new ProcessInstanceSimpleAttribute(); attr.setKey("emailSubject"); attr.setValue(subject); existingPi.addAttribute(attr); attr = new ProcessInstanceSimpleAttribute(); attr.setKey(nvl(rule.getFolderAttributeName(), "emailFolderId")); attr.setValue(folderId); existingPi.addAttribute(attr); context.getProcessInstanceDAO().saveProcessInstance(existingPi); } else { mainFolder = sessionFacade.getFolderById(folderId); } if (msg.getContent() instanceof Multipart) { Multipart multipart = (Multipart) msg.getContent(); for (int i = 0; i < multipart.getCount(); ++i) { BodyPart part = multipart.getBodyPart(i); if (rule.isOmitTextAttachments() && (part.getContentType() == null || part.getContentType().startsWith("text/"))) { logger.info("Skipping multipart attachment #" + i); continue; } sessionFacade.uploadDocument(hasText(part.getFileName()) ? part.getFileName() : "part_" + i, mainFolder, toByteArray(part.getInputStream()), part.getContentType(), null); } } else { if (!rule.isOmitTextAttachments()) sessionFacade.uploadDocument("message", mainFolder, toByteArray(msg.getInputStream()), msg.getContentType(), null); } } } } private byte[] toByteArray(InputStream inputStream) throws IOException { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int c; while ((c = inputStream.read()) >= 0) { bos.write(c); } return bos.toByteArray(); } finally { inputStream.close(); } } }