KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > content > email > EmailServices


1 /*
2  * $Id: EmailServices.java 7150 2006-03-31 07:30:43Z byersa $
3  *
4  * Copyright (c) 2001-2005 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */

25 package org.ofbiz.content.email;
26
27 import java.io.ByteArrayInputStream JavaDoc;
28 import java.io.ByteArrayOutputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.io.StringWriter JavaDoc;
33 import java.io.Writer JavaDoc;
34 import java.net.MalformedURLException JavaDoc;
35 import java.net.URL JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.ArrayList JavaDoc;
39 import java.util.Locale JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.HashMap JavaDoc;
42 import java.util.Properties JavaDoc;
43 import java.sql.Timestamp JavaDoc;
44 import java.util.Date JavaDoc;
45 import java.util.Calendar JavaDoc;
46
47 import javax.activation.DataHandler JavaDoc;
48 import javax.activation.DataSource JavaDoc;
49 import javax.mail.Message JavaDoc;
50 import javax.mail.Address JavaDoc;
51 import javax.mail.Session JavaDoc;
52 import javax.mail.Transport JavaDoc;
53 import javax.mail.internet.InternetAddress JavaDoc;
54 import javax.mail.internet.MimeBodyPart JavaDoc;
55 import javax.mail.internet.MimeMessage JavaDoc;
56 import javax.mail.internet.MimeMultipart JavaDoc;
57 import javax.xml.parsers.ParserConfigurationException JavaDoc;
58 import javax.mail.MessagingException JavaDoc;
59 import javax.mail.Multipart JavaDoc;
60 import javax.mail.Part JavaDoc;
61
62 import javolution.util.FastList;
63 import javolution.util.FastMap;
64
65 import org.apache.avalon.framework.logger.Log4JLogger;
66 import org.apache.avalon.framework.logger.Logger;
67 import org.apache.fop.apps.Driver;
68 import org.apache.fop.apps.FOPException;
69 import org.apache.fop.image.FopImageFactory;
70 import org.apache.fop.messaging.MessageHandler;
71 import org.apache.fop.tools.DocumentInputSource;
72 import org.ofbiz.base.util.Debug;
73 import org.ofbiz.base.util.GeneralException;
74 import org.ofbiz.base.util.HttpClient;
75 import org.ofbiz.base.util.HttpClientException;
76 import org.ofbiz.base.util.UtilMisc;
77 import org.ofbiz.base.util.UtilProperties;
78 import org.ofbiz.base.util.UtilValidate;
79 import org.ofbiz.base.util.UtilXml;
80 import org.ofbiz.base.util.UtilDateTime;
81 import org.ofbiz.base.util.collections.MapStack;
82 import org.ofbiz.base.util.string.FlexibleStringExpander;
83 import org.ofbiz.entity.GenericDelegator;
84 import org.ofbiz.entity.GenericValue;
85 import org.ofbiz.service.DispatchContext;
86 import org.ofbiz.service.GenericServiceException;
87 import org.ofbiz.service.LocalDispatcher;
88 import org.ofbiz.service.ServiceUtil;
89 import org.ofbiz.service.mail.MimeMessageWrapper;
90 import org.ofbiz.widget.html.HtmlScreenRenderer;
91 import org.ofbiz.widget.screen.ScreenRenderer;
92 import org.w3c.dom.Document JavaDoc;
93 import org.xml.sax.SAXException JavaDoc;
94
95 /**
96  * Email Services
97  *
98  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
99  * @author <a HREF="mailto:jonesde@ofbiz.org">David E. Jones</a>
100  * @author <a HREF="mailto:h.bakker@antwebsystems.com">Hans Bakker</a>
101  * @author <a HREF="mailto:byersa@automationgroups.com">Al Byers</a>
102  * @since 2.0
103  */

104 public class EmailServices {
105
106     public final static String JavaDoc module = EmailServices.class.getName();
107
108     protected static final HtmlScreenRenderer htmlScreenRenderer = new HtmlScreenRenderer();
109
110     /**
111      * Basic JavaMail Service
112      *@param ctx The DispatchContext that this service is operating in
113      *@param context Map containing the input parameters
114      *@return Map with the result of the service, the output parameters
115      */

116     public static Map JavaDoc sendMail(DispatchContext ctx, Map JavaDoc context) {
117           Map JavaDoc results = ServiceUtil.returnSuccess();
118         String JavaDoc subject = (String JavaDoc) context.get("subject");
119         String JavaDoc partyId = (String JavaDoc) context.get("partyId");
120         String JavaDoc body = (String JavaDoc) context.get("body");
121         List JavaDoc bodyParts = (List JavaDoc) context.get("bodyParts");
122         GenericValue userLogin = (GenericValue) context.get("userLogin");
123
124         results.put("partyId", partyId);
125         results.put("subject", subject);
126         if (UtilValidate.isNotEmpty(body)) results.put("body", body);
127         if (UtilValidate.isNotEmpty(bodyParts)) results.put("bodyParts", bodyParts);
128         results.put("userLogin", userLogin);
129
130         // first check to see if sending mail is enabled
131
String JavaDoc mailEnabled = UtilProperties.getPropertyValue("general.properties", "mail.notifications.enabled", "N");
132         if (!"Y".equalsIgnoreCase(mailEnabled)) {
133             // no error; just return as if we already processed
134
Debug.logImportant("Mail notifications disabled in general.properties; here is the context with info that would have been sent: " + context, module);
135             return results;
136         }
137         String JavaDoc sendTo = (String JavaDoc) context.get("sendTo");
138         String JavaDoc sendCc = (String JavaDoc) context.get("sendCc");
139         String JavaDoc sendBcc = (String JavaDoc) context.get("sendBcc");
140
141         // check to see if we should redirect all mail for testing
142
String JavaDoc redirectAddress = UtilProperties.getPropertyValue("general.properties", "mail.notifications.redirectTo");
143         if (UtilValidate.isNotEmpty(redirectAddress)) {
144             String JavaDoc originalRecipients = " [To: " + sendTo + ", Cc: " + sendCc + ", Bcc: " + sendBcc + "]";
145             subject = subject + originalRecipients;
146             sendTo = redirectAddress;
147             sendCc = null;
148             sendBcc = null;
149         }
150
151         String JavaDoc sendFrom = (String JavaDoc) context.get("sendFrom");
152         String JavaDoc sendType = (String JavaDoc) context.get("sendType");
153         String JavaDoc sendVia = (String JavaDoc) context.get("sendVia");
154         String JavaDoc authUser = (String JavaDoc) context.get("authUser");
155         String JavaDoc authPass = (String JavaDoc) context.get("authPass");
156         String JavaDoc contentType = (String JavaDoc) context.get("contentType");
157
158         boolean useSmtpAuth = false;
159
160         // define some default
161
if (sendType == null || sendType.equals("mail.smtp.host")) {
162             sendType = "mail.smtp.host";
163             if (sendVia == null || sendVia.length() == 0) {
164                 sendVia = UtilProperties.getPropertyValue("general.properties", "mail.smtp.relay.host", "localhost");
165             }
166             if (authUser == null || authUser.length() == 0) {
167                 authUser = UtilProperties.getPropertyValue("general.properties", "mail.smtp.auth.user");
168             }
169             if (authPass == null || authPass.length() == 0) {
170                 authPass = UtilProperties.getPropertyValue("general.properties", "mail.smtp.auth.password");
171             }
172             if (authUser != null && authUser.length() > 0) {
173                 useSmtpAuth = true;
174             }
175         } else if (sendVia == null) {
176             return ServiceUtil.returnError("Parameter sendVia is required when sendType is not mail.smtp.host");
177         }
178
179
180         if (contentType == null) {
181             contentType = "text/html";
182         }
183
184         if (UtilValidate.isNotEmpty(bodyParts)) {
185             contentType = "multipart/mixed";
186         }
187         results.put("contentType", contentType);
188
189         try {
190             Properties JavaDoc props = System.getProperties();
191             props.put(sendType, sendVia);
192             if (useSmtpAuth) {
193                 props.put("mail.smtp.auth", "true");
194             }
195
196             Session JavaDoc session = Session.getInstance(props);
197
198             MimeMessage JavaDoc mail = new MimeMessage JavaDoc(session);
199             mail.setFrom(new InternetAddress JavaDoc(sendFrom));
200             mail.setSubject(subject);
201             mail.addRecipients(Message.RecipientType.TO, sendTo);
202
203             if (UtilValidate.isNotEmpty(sendCc)) {
204                 mail.addRecipients(Message.RecipientType.CC, sendCc);
205             }
206             if (UtilValidate.isNotEmpty(sendBcc)) {
207                 mail.addRecipients(Message.RecipientType.BCC, sendBcc);
208             }
209
210             if (UtilValidate.isNotEmpty(bodyParts)) {
211                 // check for multipart message (with attachments)
212
// BodyParts contain a list of Maps items containing content(String) and type(String) of the attachement
213
MimeMultipart JavaDoc mp = new MimeMultipart JavaDoc();
214                 Debug.logInfo(bodyParts.size() + " multiparts found",module);
215                 Iterator JavaDoc bodyPartIter = bodyParts.iterator();
216                 while (bodyPartIter.hasNext()) {
217                     Map JavaDoc bodyPart = (Map JavaDoc) bodyPartIter.next();
218                     Object JavaDoc bodyPartContent = bodyPart.get("content");
219                     MimeBodyPart JavaDoc mbp = new MimeBodyPart JavaDoc();
220
221                     if (bodyPartContent instanceof String JavaDoc) {
222                         StringDataSource sdr = new StringDataSource((String JavaDoc) bodyPartContent, (String JavaDoc) bodyPart.get("type"));
223                         Debug.logInfo("part of type: " + bodyPart.get("type") + " and size: " + bodyPart.get("content").toString().length() , module);
224                         mbp.setDataHandler(new DataHandler JavaDoc(sdr));
225                     } else if (bodyPartContent instanceof byte[]) {
226                         ByteArrayDataSource bads = new ByteArrayDataSource((byte[]) bodyPartContent, (String JavaDoc) bodyPart.get("type"));
227                         Debug.logInfo("part of type: " + bodyPart.get("type") + " and size: " + ((byte[]) bodyPartContent).length , module);
228                         mbp.setDataHandler(new DataHandler JavaDoc(bads));
229                     } else {
230                         mbp.setDataHandler(new DataHandler JavaDoc(bodyPartContent, (String JavaDoc) bodyPart.get("type")));
231                     }
232
233                     String JavaDoc fileName = (String JavaDoc) bodyPart.get("filename");
234                     if (fileName != null) {
235                         mbp.setFileName(fileName);
236                     }
237                     mp.addBodyPart(mbp);
238                 }
239                 mail.setContent(mp);
240                 mail.saveChanges();
241             } else {
242                 // create the singelpart message
243
mail.setContent(body, contentType);
244                 mail.saveChanges();
245             }
246
247             Transport JavaDoc trans = session.getTransport("smtp");
248             if (!useSmtpAuth) {
249                 trans.connect();
250             } else {
251                 trans.connect(sendVia, authUser, authPass);
252             }
253             trans.sendMessage(mail, mail.getAllRecipients());
254             trans.close();
255         } catch (Exception JavaDoc e) {
256             String JavaDoc errMsg = "Cannot send email message to [" + sendTo + "] from [" + sendFrom + "] cc [" + sendCc + "] bcc [" + sendBcc + "] subject [" + subject + "]";
257             Debug.logError(e, errMsg, module);
258             Debug.logError(e, "Email message that could not be sent to [" + sendTo + "] had context: " + context, module);
259             return ServiceUtil.returnError(errMsg);
260         }
261         return results;
262     }
263
264     /**
265      * JavaMail Service that gets body content from a URL
266      *@param ctx The DispatchContext that this service is operating in
267      *@param context Map containing the input parameters
268      *@return Map with the result of the service, the output parameters
269      */

270     public static Map JavaDoc sendMailFromUrl(DispatchContext ctx, Map JavaDoc context) {
271         // pretty simple, get the content and then call the sendMail method below
272
String JavaDoc bodyUrl = (String JavaDoc) context.remove("bodyUrl");
273         Map JavaDoc bodyUrlParameters = (Map JavaDoc) context.remove("bodyUrlParameters");
274
275         URL JavaDoc url = null;
276
277         try {
278             url = new URL JavaDoc(bodyUrl);
279         } catch (MalformedURLException JavaDoc e) {
280             Debug.logWarning(e, module);
281             return ServiceUtil.returnError("Malformed URL: " + bodyUrl + "; error was: " + e.toString());
282         }
283
284         HttpClient httpClient = new HttpClient(url, bodyUrlParameters);
285         String JavaDoc body = null;
286
287         try {
288             body = httpClient.post();
289         } catch (HttpClientException e) {
290             Debug.logWarning(e, module);
291             return ServiceUtil.returnError("Error getting content: " + e.toString());
292         }
293
294         context.put("body", body);
295         Map JavaDoc result = sendMail(ctx, context);
296
297         result.put("body", body);
298         return result;
299     }
300
301     /**
302      * JavaMail Service that gets body content from a Screen Widget
303      * defined in the product store record and if available as attachment also.
304      *@param dctx The DispatchContext that this service is operating in
305      *@param serviceContext Map containing the input parameters
306      *@return Map with the result of the service, the output parameters
307      */

308     public static Map JavaDoc sendMailFromScreen(DispatchContext dctx, Map JavaDoc serviceContext) {
309         LocalDispatcher dispatcher = dctx.getDispatcher();
310         String JavaDoc webSiteId = (String JavaDoc) serviceContext.remove("webSiteId");
311         String JavaDoc bodyText = (String JavaDoc) serviceContext.remove("bodyText");
312         String JavaDoc bodyScreenUri = (String JavaDoc) serviceContext.remove("bodyScreenUri");
313         String JavaDoc xslfoAttachScreenLocation = (String JavaDoc) serviceContext.remove("xslfoAttachScreenLocation");
314         Map JavaDoc bodyParameters = (Map JavaDoc) serviceContext.remove("bodyParameters");
315         String JavaDoc partyId = (String JavaDoc) bodyParameters.get("partyId");
316         NotificationServices.setBaseUrl(dctx.getDelegator(), webSiteId, bodyParameters);
317         String JavaDoc contentType = (String JavaDoc) serviceContext.remove("contentType");
318
319         StringWriter JavaDoc bodyWriter = new StringWriter JavaDoc();
320
321         MapStack screenContext = MapStack.create();
322         ScreenRenderer screens = new ScreenRenderer(bodyWriter, screenContext, htmlScreenRenderer);
323         screens.populateContextForService(dctx, bodyParameters);
324         screenContext.putAll(bodyParameters);
325
326         if (bodyScreenUri != null) {
327             try {
328                 screens.render(bodyScreenUri);
329             } catch (GeneralException e) {
330                 String JavaDoc errMsg = "Error rendering screen for email: " + e.toString();
331                 Debug.logError(e, errMsg, module);
332                 return ServiceUtil.returnError(errMsg);
333             } catch (IOException JavaDoc e) {
334                 String JavaDoc errMsg = "Error rendering screen for email: " + e.toString();
335                 Debug.logError(e, errMsg, module);
336                 return ServiceUtil.returnError(errMsg);
337             } catch (SAXException JavaDoc e) {
338                 String JavaDoc errMsg = "Error rendering screen for email: " + e.toString();
339                 Debug.logError(e, errMsg, module);
340                 return ServiceUtil.returnError(errMsg);
341             } catch (ParserConfigurationException JavaDoc e) {
342                 String JavaDoc errMsg = "Error rendering screen for email: " + e.toString();
343                 Debug.logError(e, errMsg, module);
344                 return ServiceUtil.returnError(errMsg);
345             }
346         }
347         
348         boolean isMultiPart = false;
349         
350         // check if attachement screen location passed in
351
if (UtilValidate.isNotEmpty(xslfoAttachScreenLocation)) {
352             isMultiPart = true;
353             // start processing fo pdf attachment
354
try {
355                 Writer JavaDoc writer = new StringWriter JavaDoc();
356                 MapStack screenContextAtt = MapStack.create();
357                 // substitute the freemarker variables...
358
ScreenRenderer screensAtt = new ScreenRenderer(writer, screenContext, htmlScreenRenderer);
359                 screensAtt.populateContextForService(dctx, bodyParameters);
360                 screenContextAtt.putAll(bodyParameters);
361                 screensAtt.render(xslfoAttachScreenLocation);
362                 
363                 /*
364                 try { // save generated fo file for debugging
365                     String buf = writer.toString();
366                     java.io.FileWriter fw = new java.io.FileWriter(new java.io.File("/tmp/file1.xml"));
367                     fw.write(buf.toString());
368                     fw.close();
369                 } catch (IOException e) {
370                     Debug.logError(e, "Couldn't save xsl-fo xml debug file: " + e.toString(), module);
371                 }
372                 */

373                 
374                 // configure logging for the FOP
375
Logger logger = new Log4JLogger(Debug.getLogger(module));
376                 MessageHandler.setScreenLogger(logger);
377                 
378                 // load the FOP driver
379
Driver driver = new Driver();
380                 driver.setRenderer(Driver.RENDER_PDF);
381                 driver.setLogger(logger);
382                 
383                 // read the XSL-FO XML into the W3 Document
384
Document JavaDoc xslfo = UtilXml.readXmlDocument(writer.toString());
385
386                 // create the in/output stream for the generation
387
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
388                 driver.setOutputStream(baos);
389                 driver.setInputSource(new DocumentInputSource(xslfo));
390                 
391                 // and generate the PDF
392
driver.run();
393                 FopImageFactory.resetCache();
394                 baos.flush();
395                 baos.close();
396
397                 /*
398                 try { // save generated pdf file for debugging
399                     FileOutputStream fos = new FileOutputStream(new java.io.File("/tmp/file2.pdf"));
400                     baos.writeTo(fos);
401                     fos.close();
402                 } catch (IOException e) {
403                     Debug.logError(e, "Couldn't save xsl-fo pdf debug file: " + e.toString(), module);
404                 }
405                 */

406
407                 // store in the list of maps for sendmail....
408
List JavaDoc bodyParts = FastList.newInstance();
409                 if (bodyText != null) {
410                     bodyText = FlexibleStringExpander.expandString(bodyText, screenContext, (Locale JavaDoc) screenContext.get("locale"));
411                     bodyParts.add(UtilMisc.toMap("content", bodyText, "type", "text/html"));
412                 } else {
413                     bodyParts.add(UtilMisc.toMap("content", bodyWriter.toString(), "type", "text/html"));
414                 }
415                 bodyParts.add(UtilMisc.toMap("content", baos.toByteArray(), "type", "application/pdf", "filename", "Details.pdf"));
416                 serviceContext.put("bodyParts", bodyParts);
417             } catch (GeneralException ge) {
418                 String JavaDoc errMsg = "Error rendering PDF attachment for email: " + ge.toString();
419                 Debug.logError(ge, errMsg, module);
420                 return ServiceUtil.returnError(errMsg);
421             } catch (IOException JavaDoc ie) {
422                 String JavaDoc errMsg = "Error rendering PDF attachment for email: " + ie.toString();
423                 Debug.logError(ie, errMsg, module);
424                 return ServiceUtil.returnError(errMsg);
425             } catch (FOPException fe) {
426                 String JavaDoc errMsg = "Error rendering PDF attachment for email: " + fe.toString();
427                 Debug.logError(fe, errMsg, module);
428                 return ServiceUtil.returnError(errMsg);
429             } catch (SAXException JavaDoc se) {
430                 String JavaDoc errMsg = "Error rendering PDF attachment for email: " + se.toString();
431                 Debug.logError(se, errMsg, module);
432                 return ServiceUtil.returnError(errMsg);
433             } catch (ParserConfigurationException JavaDoc pe) {
434                 String JavaDoc errMsg = "Error rendering PDF attachment for email: " + pe.toString();
435                 Debug.logError(pe, errMsg, module);
436                 return ServiceUtil.returnError(errMsg);
437             }
438         } else {
439             isMultiPart = false;
440             // store body and type for single part message in the context.
441
if (bodyText != null) {
442                 bodyText = FlexibleStringExpander.expandString(bodyText, screenContext, (Locale JavaDoc) screenContext.get("locale"));
443                 serviceContext.put("body", bodyText);
444             } else {
445                 serviceContext.put("body", bodyWriter.toString());
446             }
447
448             // Only override the default contentType in case of plaintext, since other contentTypes may be multipart
449
// and would require specific handling.
450
if (contentType != null && contentType.equalsIgnoreCase("text/plain")) {
451                 serviceContext.put("contentType", "text/plain");
452             } else {
453                 serviceContext.put("contentType", "text/html");
454             }
455         }
456         
457         // also expand the subject at this point, just in case it has the FlexibleStringExpander syntax in it...
458
String JavaDoc subject = (String JavaDoc) serviceContext.remove("subject");
459         subject = FlexibleStringExpander.expandString(subject, screenContext, (Locale JavaDoc) screenContext.get("locale"));
460         serviceContext.put("subject", subject);
461         serviceContext.put("partyId", partyId);
462
463         if (Debug.verboseOn()) Debug.logVerbose("sendMailFromScreen sendMail context: " + serviceContext, module);
464         Map JavaDoc result = ServiceUtil.returnSuccess();
465       
466         try {
467             if (isMultiPart) {
468                 dispatcher.runSync("sendMailMultiPart", serviceContext);
469             } else {
470                 dispatcher.runSync("sendMail", serviceContext);
471             }
472         } catch (Exception JavaDoc e) {
473             String JavaDoc errMsg = "Error send email :" + e.toString();
474             Debug.logError(e, errMsg, module);
475             return ServiceUtil.returnError(errMsg);
476         }
477         result.put("body", bodyWriter.toString());
478         return result;
479     }
480     
481     /**
482      * Store email as communication event
483      *@param dctx The DispatchContext that this service is operating in
484      *@param serviceContext Map containing the input parameters
485      *@return Map with the result of the service, the output parameters
486      */

487     public static Map JavaDoc storeEmailAsCommunication(DispatchContext dctx, Map JavaDoc serviceContext) {
488         LocalDispatcher dispatcher = dctx.getDispatcher();
489         GenericValue userLogin = (GenericValue) serviceContext.get("userLogin");
490         
491         String JavaDoc subject = (String JavaDoc) serviceContext.get("subject");
492         String JavaDoc body = (String JavaDoc) serviceContext.get("body");
493         String JavaDoc partyId = (String JavaDoc) serviceContext.get("partyId");
494         String JavaDoc communicationEventId = (String JavaDoc) serviceContext.get("communicationEventId");
495         String JavaDoc contentType = (String JavaDoc) serviceContext.get("contentType");
496         
497         // only create a new communication event if the email is not already associated with one
498
if (communicationEventId == null) {
499             String JavaDoc partyIdFrom = (String JavaDoc) userLogin.get("partyId");
500             Map JavaDoc commEventMap = FastMap.newInstance();
501             commEventMap.put("communicationEventTypeId", "EMAIL_COMMUNICATION");
502             commEventMap.put("statusId", "COM_COMPLETE");
503             commEventMap.put("contactMechTypeId", "EMAIL_ADDRESS");
504             commEventMap.put("partyIdFrom", partyIdFrom);
505             commEventMap.put("partyIdTo", partyId);
506             commEventMap.put("subject", subject);
507             commEventMap.put("content", body);
508             commEventMap.put("userLogin", userLogin);
509             commEventMap.put("contentMimeTypeId", contentType);
510             try {
511                 dispatcher.runSync("createCommunicationEvent", commEventMap);
512             } catch (Exception JavaDoc e) {
513                 Debug.logError(e, "Cannot store email as communication event", module);
514                 return ServiceUtil.returnError("Cannot store email as communication event; see logs");
515             }
516         }
517         
518         return ServiceUtil.returnSuccess();
519     }
520
521     /** class to create a file in memory required for sending as an attachment */
522     public static class StringDataSource implements DataSource JavaDoc {
523         private String JavaDoc contentType;
524         private ByteArrayOutputStream JavaDoc contentArray;
525         
526         public StringDataSource(String JavaDoc content, String JavaDoc contentType) throws IOException JavaDoc {
527             this.contentType = contentType;
528             contentArray = new ByteArrayOutputStream JavaDoc();
529             contentArray.write(content.getBytes("iso-8859-1"));
530             contentArray.flush();
531             contentArray.close();
532         }
533         
534         public String JavaDoc getContentType() {
535             return contentType == null ? "application/octet-stream" : contentType;
536         }
537  
538         public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
539             return new ByteArrayInputStream JavaDoc(contentArray.toByteArray());
540         }
541  
542         public String JavaDoc getName() {
543             return "stringDatasource";
544         }
545  
546         public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
547             throw new IOException JavaDoc("Cannot write to this read-only resource");
548         }
549     }
550
551     /** class to create a file in memory required for sending as an attachment */
552     public static class ByteArrayDataSource implements DataSource JavaDoc {
553         private String JavaDoc contentType;
554         private byte[] contentArray;
555         
556         public ByteArrayDataSource(byte[] content, String JavaDoc contentType) throws IOException JavaDoc {
557             this.contentType = contentType;
558             this.contentArray = content;
559         }
560         
561         public String JavaDoc getContentType() {
562             return contentType == null ? "application/octet-stream" : contentType;
563         }
564  
565         public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
566             return new ByteArrayInputStream JavaDoc(contentArray);
567         }
568  
569         public String JavaDoc getName() {
570             return "ByteArrayDataSource";
571         }
572  
573         public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
574             throw new IOException JavaDoc("Cannot write to this read-only resource");
575         }
576     }
577     
578     /*
579      * Helper method to retrieve the party information from the first email address of the Address[] specified.
580      */

581     private static Map JavaDoc getParyInfoFromEmailAddress(Address JavaDoc [] addresses, GenericValue userLogin, LocalDispatcher dispatcher) throws GenericServiceException
582     {
583         InternetAddress JavaDoc emailAddress = null;
584         Map JavaDoc map = null;
585         Map JavaDoc result = null;
586         
587         if (addresses.length > 0) {
588             Address JavaDoc addr = addresses[0];
589             if (addr instanceof InternetAddress JavaDoc) {
590                 emailAddress = (InternetAddress JavaDoc)addr;
591             }
592         }
593         
594         if (!UtilValidate.isEmpty(emailAddress)) {
595             map = new HashMap JavaDoc();
596             map.put("address", emailAddress.getAddress());
597             map.put("personal", emailAddress.getPersonal());
598             map.put("userLogin", userLogin);
599             result = dispatcher.runSync("findPartyFromEmailAddress", map);
600         }
601         
602         return result;
603     }
604     
605     /*
606      * Calls findPartyFromEmailAddress service and returns a List of the results for the array of addresses
607      */

608     private static List JavaDoc buildListOfPartyInfoFromEmailAddresses(Address JavaDoc [] addresses, GenericValue userLogin, LocalDispatcher dispatcher) throws GenericServiceException
609     {
610         InternetAddress JavaDoc emailAddress = null;
611         Address JavaDoc addr = null;
612         Map JavaDoc map = null;
613         Map JavaDoc result = null;
614         List JavaDoc tempResults = new ArrayList JavaDoc();
615         
616         if (addresses != null) {
617             for (int i = 0; i < addresses.length; i++) {
618                 addr = addresses[i];
619                 if (addr instanceof InternetAddress JavaDoc) {
620                     emailAddress = (InternetAddress JavaDoc)addr;
621                     
622                     if (!UtilValidate.isEmpty(emailAddress)) {
623                         map = new HashMap JavaDoc();
624                         map.put("address", emailAddress.getAddress());
625                         map.put("personal", emailAddress.getPersonal());
626                         map.put("userLogin", userLogin);
627                         result = dispatcher.runSync("findPartyFromEmailAddress", map);
628                         
629                         tempResults.add(result);
630                     }
631                 }
632             }
633         }
634         
635         return tempResults;
636     }
637     
638     /*
639      * Helper method to retrieve a combined list of party information from to, cc, and bcc email addresses
640      */

641     private static List JavaDoc getListOfParyInfoFromEmailAddresses(Address JavaDoc [] addressesTo, Address JavaDoc [] addressesCC, Address JavaDoc [] addressesBCC, GenericValue userLogin, LocalDispatcher dispatcher) throws GenericServiceException
642     {
643         List JavaDoc allResults = new ArrayList JavaDoc();
644         
645         //Get Party Info for To email addresses
646
allResults.addAll(buildListOfPartyInfoFromEmailAddresses(addressesTo, userLogin, dispatcher));
647         
648         //Get Party Info for CC email addresses
649
allResults.addAll(buildListOfPartyInfoFromEmailAddresses(addressesCC, userLogin, dispatcher));
650         
651         //Get Party Info for BCC email addresses
652
allResults.addAll(buildListOfPartyInfoFromEmailAddresses(addressesBCC, userLogin, dispatcher));
653         
654         return allResults;
655     }
656     
657     /**
658      * This service is the main one for processing incoming emails.
659      *
660      * Its only argument is a wrapper for the JavaMail MimeMessage object.
661      * From this object, all the fields, headers and content of the message can be accessed.
662      *
663      * The first thing this service does is try to discover the partyId of the message sender
664      * by doing a reverse find on the email address. It uses the findPartyFromEmailAddress service to do this.
665      *
666      * It then creates a CommunicationEvent entity by calling the createCommunicationEvent service using the appropriate fields from the email and the
667      * discovered partyId, if it exists, as the partyIdFrom. Note that it sets the communicationEventTypeId
668      * field to AUTO_EMAIL_COMM. This is useful for tracking email generated communications.
669      *
670      * The service tries to find appropriate content for inclusion in the CommunicationEvent.content field.
671      * If the contentType of the content starts with "text", the getContent() call returns a string and it is used.
672      * If the contentType starts with "multipart", then the "parts" of the content are iterated thru and the first
673      * one of mime type, "text/..." is used.
674      *
675      * If the contentType has a value of "multipart" then the parts of the content (except the one used in the main
676      * CommunicationEvent.content field) are cycled thru and attached to the CommunicationEvent entity using the
677      * createCommContentDataResource service. This happens in the EmailWorker.addAttachmentsToCommEvent method.
678      *
679      * -Al Byers
680      * @param dctx
681      * @param context
682      * @return
683      */

684     public static Map JavaDoc storeIncomingEmail(DispatchContext dctx, Map JavaDoc context) {
685         
686         GenericDelegator delegator = dctx.getDelegator();
687         LocalDispatcher dispatcher = dctx.getDispatcher();
688         MimeMessageWrapper wrapper = (MimeMessageWrapper) context.get("messageWrapper");
689         MimeMessage JavaDoc message = wrapper.getMessage();
690         Timestamp JavaDoc nowTimestamp = UtilDateTime.nowTimestamp();
691         GenericValue userLogin = (GenericValue) context.get("userLogin");
692         String JavaDoc partyIdTo = null;
693         String JavaDoc partyIdFrom = null;
694         String JavaDoc contentType = null;
695         String JavaDoc content = null;
696         String JavaDoc communicationEventId = null;
697         String JavaDoc contactMechIdFrom = null;
698         String JavaDoc contactMechIdTo = null;
699         
700         Map JavaDoc result = null;
701         try {
702             String JavaDoc contentTypeRaw = message.getContentType();
703             int idx = contentTypeRaw.indexOf(";");
704             contentType = contentTypeRaw.substring(0, idx);
705             Address JavaDoc [] addressesFrom = message.getFrom();
706             Address JavaDoc [] addressesTo = message.getRecipients(MimeMessage.RecipientType.TO);
707             Address JavaDoc [] addressesCC = message.getRecipients(MimeMessage.RecipientType.CC);
708             Address JavaDoc [] addressesBCC = message.getRecipients(MimeMessage.RecipientType.BCC);
709             Debug.logInfo("Processing Incoming Email message from: " + addressesFrom[0].toString() + " to: " + addressesTo[0].toString(), module);
710
711             // ignore the message when the spam status = yes
712
String JavaDoc spamHeaderName = UtilProperties.getPropertyValue("general.properties", "mail.spam.name", "N");
713             String JavaDoc configHeaderValue = UtilProperties.getPropertyValue("general.properties", "mail.spam.value");
714             // only execute when config file has been set && header variable found
715
if (!spamHeaderName.equals("N") && message.getHeader(spamHeaderName) != null) {
716                 String JavaDoc msgHeaderValue = message.getHeader(spamHeaderName)[0];
717                 if(msgHeaderValue != null && msgHeaderValue.startsWith(configHeaderValue)) {
718                     Debug.logInfo("Incoming Email message ignored, was detected by external spam checker", module);
719                     return ServiceUtil.returnSuccess(" Message Ignored: detected by external spam checker");
720                 }
721             }
722             
723             // if no 'from' addresses specified ignore the message
724
if (addressesFrom == null) {
725                 Debug.logInfo("Incoming Email message ignored, had not 'from' email address", module);
726                 return ServiceUtil.returnSuccess(" Message Ignored: no 'From' address specified");
727             }
728
729             
730             result = getParyInfoFromEmailAddress(addressesFrom, userLogin, dispatcher);
731             partyIdFrom = (String JavaDoc)result.get("partyId");
732             contactMechIdFrom = (String JavaDoc)result.get("contactMechId");
733             
734             List JavaDoc allResults = getListOfParyInfoFromEmailAddresses(addressesTo, addressesCC, addressesBCC, userLogin, dispatcher);
735             Iterator JavaDoc itr = allResults.iterator();
736             
737             //Get the first address from the list - this is the partyIdTo field of the CommunicationEvent
738
if ((allResults != null) && (allResults.size() > 0)) {
739                 Map JavaDoc firstAddressTo = (Map JavaDoc) itr.next();
740                 partyIdTo = (String JavaDoc)firstAddressTo.get("partyId");
741                 contactMechIdTo = (String JavaDoc)firstAddressTo.get("contactMechId");
742             }
743             
744             Map JavaDoc commEventMap = new HashMap JavaDoc();
745             commEventMap.put("communicationEventTypeId", "AUTO_EMAIL_COMM");
746             commEventMap.put("contactMechTypeId", "EMAIL_ADDRESS");
747             String JavaDoc subject = message.getSubject();
748             commEventMap.put("subject", subject);
749             
750             commEventMap.put("entryDate", nowTimestamp);
751             
752             //Set sent and received dates
753
commEventMap.put("datetimeStarted", UtilDateTime.toTimestamp(message.getSentDate()));
754             commEventMap.put("datetimeEnded", UtilDateTime.toTimestamp(message.getReceivedDate()));
755
756             int contentIndex = -1; // identifies which part of a multi-part message is the body or content
757
Multipart JavaDoc multipart = null;
758             if (contentType.startsWith("text")) {
759                 // if a simple text message, the message content is the content for CommunicationEvent
760
content = (String JavaDoc)message.getContent();
761                 commEventMap.put("contentMimeTypeId", contentType);
762             } else if (contentType.startsWith("multipart")) {
763                 multipart = (Multipart JavaDoc)message.getContent();
764                 int multipartCount = multipart.getCount();
765                 
766                 // loop through all the parts of a multi-part email to find the body and the attachments
767
for (int i=0; i < multipartCount; i++) {
768                     Part JavaDoc part = multipart.getBodyPart(i);
769                     String JavaDoc thisContentTypeRaw = part.getContentType();
770                     int idx2 = thisContentTypeRaw.indexOf(";");
771                     String JavaDoc thisContentType = thisContentTypeRaw.substring(0, idx2);
772                     String JavaDoc disposition = part.getDisposition();
773                     
774                     // this part is the body
775
if ((disposition == null) && (i == 0))
776                     {
777                         if (part.getContent() != null) {
778                             // but the body can be multi-part as well, such as a text/plain and text/html
779
// MS Outlook Express for example will send a multi-part email with a nested body of text/plain and text/html
780
if (thisContentType.startsWith("text")) {
781                                 // if the body is a "text/" then that's the content
782
content = (String JavaDoc)part.getContent();
783                             }
784                             else {
785                                 // otherwise, loop through all the parts of the body and look for the text/html to be the content
786
if(part.getContent() instanceof Multipart JavaDoc) {
787                                     Multipart JavaDoc components = (Multipart JavaDoc)part.getContent();
788                                     int numOfComponents = components.getCount();
789                                     
790                                     for (int j = 0; j < numOfComponents; j++) {
791                                         Part JavaDoc component = components.getBodyPart(j);
792                                         String JavaDoc entireContent = component.getContentType();
793                                         int idxOfSemiColon = entireContent.indexOf(";");
794                                         String JavaDoc requiredContent = entireContent.substring(0, idxOfSemiColon);
795                                         
796                                         if (requiredContent.startsWith("text/html")) {
797                                             content = (String JavaDoc)component.getContent();
798                                             
799                                             //match-found - the rest is text/plain
800
break;
801                                         }
802                                     }
803                                 }
804                             }
805                         }
806                         
807                         if (UtilValidate.isNotEmpty(content)) {
808                             contentIndex = i;
809                             commEventMap.put("contentMimeTypeId", thisContentType);
810                             break;
811                         }
812                     } else if ((disposition != null)
813                          && (disposition.equals(Part.ATTACHMENT) || disposition.equals(Part.INLINE))
814                          && thisContentType.startsWith("text"))
815                     {
816                         content = (String JavaDoc)part.getContent();
817                         contentIndex = i;
818                         commEventMap.put("contentMimeTypeId", thisContentType);
819                         break;
820                     }
821                 }
822             }
823             commEventMap.put("content", content);
824             
825             // store a note when the to/from emails are not associated with any parties in the system
826
String JavaDoc commNote = "";
827             if (partyIdFrom != null) {
828                 commEventMap.put("partyIdFrom", partyIdFrom);
829                 commEventMap.put("contactMechIdFrom", contactMechIdFrom);
830                 commEventMap.put("contactMechIdTo", contactMechIdTo);
831                 commEventMap.put("statusId", "COM_ENTERED");
832             } else {
833                 commNote += "Sent from: " + ((InternetAddress JavaDoc)addressesFrom[0]).getAddress() + "; ";
834             }
835
836             if (partyIdTo != null) {
837                 commEventMap.put("partyIdTo", partyIdTo);
838                 commEventMap.put("contactMechIdTo", contactMechIdTo);
839             } else {
840                 commNote += "Sent to: " + ((InternetAddress JavaDoc)addressesTo[0]).getAddress() + "; ";
841             }
842
843             if (partyIdTo != null && partyIdFrom != null) {
844                 commEventMap.put("statusId", "COM_ENTERED");
845             } else {
846                 commEventMap.put("statusId", "COM_UNKNOWN_PARTY");
847             }
848             
849             if (!("".equals(commNote))) {
850                 commEventMap.put("note", commNote);
851             }
852             
853             commEventMap.put("userLogin", userLogin);
854             result = dispatcher.runSync("createCommunicationEvent", commEventMap);
855             communicationEventId = (String JavaDoc)result.get("communicationEventId");
856             if (contentType.startsWith("multipart")) {
857                 int attachmentCount = EmailWorker.addAttachmentsToCommEvent(message, communicationEventId, contentIndex, dispatcher, userLogin);
858                 if (Debug.infoOn()) Debug.logInfo(attachmentCount + " attachments added to CommunicationEvent:" + communicationEventId,module);
859             }
860             
861             //For all other addresses create a CommunicationEventRole
862
while (itr.hasNext()) {
863                 Map JavaDoc address = (Map JavaDoc) itr.next();
864                 String JavaDoc partyId = (String JavaDoc)address.get("partyId");
865                 
866                 // It's not clear what the "role" of this communication event should be, so we'll just put _NA_
867
//Check if "_NA_" role exists for the partyId. If not, then first associate that role with the partyId
868
GenericValue partyRole = delegator.findByPrimaryKey("PartyRole", UtilMisc.toMap("partyId", partyId, "roleTypeId", "_NA_"));
869                 if (partyRole == null) {
870                     dispatcher.runSync("createPartyRole", UtilMisc.toMap("partyId", partyId, "roleTypeId", "_NA_", "userLogin", userLogin));
871                 }
872                 Map JavaDoc input = UtilMisc.toMap("communicationEventId", communicationEventId, "partyId", partyId, "roleTypeId", "_NA_", "userLogin", userLogin, "contactMechId", (String JavaDoc)address.get("contactMechId"));
873                 dispatcher.runSync("createCommunicationEventRole", input);
874             }
875             
876             Map JavaDoc results = ServiceUtil.returnSuccess();
877             results.put("communicationEventId", communicationEventId);
878             return results;
879         } catch (MessagingException JavaDoc e) {
880             Debug.logError(e, module);
881             return ServiceUtil.returnError(e.getMessage());
882         } catch (GenericServiceException e) {
883             Debug.logError(e, module);
884             return ServiceUtil.returnError(e.getMessage());
885         } catch (IOException JavaDoc e) {
886             Debug.logError(e, module);
887             return ServiceUtil.returnError(e.getMessage());
888         } catch (Exception JavaDoc e) {
889             Debug.logError(e, module);
890             return ServiceUtil.returnError(e.getMessage());
891         }
892     }
893 }
894
Popular Tags