1 16 package org.apache.cocoon.mail.transformation; 17 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.util.ArrayList ; 21 import java.util.Iterator ; 22 import java.util.List ; 23 import java.util.Map ; 24 import java.util.Properties ; 25 import java.util.StringTokenizer ; 26 27 import javax.activation.DataHandler ; 28 import javax.activation.DataSource ; 29 import javax.mail.Address ; 30 import javax.mail.BodyPart ; 31 import javax.mail.Message ; 32 import javax.mail.Multipart ; 33 import javax.mail.SendFailedException ; 34 import javax.mail.Session ; 35 import javax.mail.Transport ; 36 import javax.mail.internet.InternetAddress ; 37 import javax.mail.internet.MimeBodyPart ; 38 import javax.mail.internet.MimeMessage ; 39 import javax.mail.internet.MimeMultipart ; 40 41 import org.apache.avalon.framework.configuration.Configuration; 42 import org.apache.avalon.framework.configuration.ConfigurationException; 43 import org.apache.avalon.framework.parameters.Parameters; 44 import org.apache.cocoon.ProcessingException; 45 import org.apache.cocoon.environment.SourceResolver; 46 import org.apache.cocoon.mail.datasource.SourceDataSource; 47 import org.apache.cocoon.transformation.AbstractSAXTransformer; 48 import org.apache.excalibur.source.Source; 49 import org.xml.sax.Attributes ; 50 import org.xml.sax.SAXException ; 51 import org.xml.sax.helpers.AttributesImpl ; 52 53 202 public class SendMailTransformer extends AbstractSAXTransformer { 203 204 207 public static final String NAMESPACE = "http://apache.org/cocoon/transformation/sendmail"; 208 public static final String ELEMENT_SENDMAIL = "sendmail"; 209 public static final String ELEMENT_SMTPHOST = "smtphost"; 210 public static final String ELEMENT_SMTPPORT = "smtpport"; 211 public static final String ELEMENT_MAILFROM = "from"; 212 public static final String ELEMENT_MAILTO = "to"; 213 public static final String ELEMENT_REPLYTO = "reply-to"; 214 public static final String ELEMENT_MAILSUBJECT = "subject"; 215 public static final String ELEMENT_MAILBODY = "body"; 216 public static final String ELEMENT_ATTACHMENT = "attachment"; 217 public static final String ELEMENT_ATTACHMENT_CONTENT = "content"; 218 public static final String ELEMENT_EMAIL_PREFIX = "email"; 219 public static final String ELEMENT_ERROR = "error"; 220 public static final String ELEMENT_SUCCESS = "success"; 221 public static final String ELEMENT_FAILURE = "failure"; 222 public static final String ELEMENT_RESULT = "result"; 223 224 public static final String DEFAULT_BODY_MIMETYPE = "text/html"; 225 226 229 protected static final int MODE_NONE = 0; 230 protected static final int MODE_SMTPHOST = 1; 231 protected static final int MODE_FROM = 2; 232 protected static final int MODE_TO = 3; 233 protected static final int MODE_SUBJECT = 4; 234 protected static final int MODE_BODY = 5; 235 protected static final int MODE_ATTACHMENT = 6; 236 protected static final int MODE_ATTACHMENT_CONTENT = 7; 237 protected static final int MODE_REPLY_TO = 8; 238 protected static final int MODE_SMTPPORT = 9; 239 240 243 public final static String PARAM_SMTPHOST = "smtphost"; 244 public final static String PARAM_SMTPPORT = "smtpport"; 245 public final static String PARAM_FROM = "from"; 246 public final static String PARAM_TO = "to"; 247 public final static String PARAM_REPLY_TO = "reply-to"; 248 public final static String PARAM_SUBJECT = "subject"; 249 public final static String PARAM_BODY = "body"; 250 public final static String PARAM_SENDPARTIAL = "sendpartial"; 251 protected int mode; 252 253 256 protected List toAddresses; 257 protected List replyToAddresses; 258 protected List defaultToAddresses; 259 protected List defaultReplyToAddresses; 260 protected List attachments; 261 protected String subject; 262 protected String body; 263 protected String bodyURI; 264 protected String bodyMimeType; 265 protected String mailHost; 266 protected int mailPort; 267 protected String fromAddress; 268 protected AttachmentDescriptor attachmentDescriptor; 269 protected int port; 270 protected String contextPath; 271 protected boolean sendPartial; 272 protected Message smtpMessage; 273 274 protected String defaultSmtpHost; 275 protected int defaultSmtpPort; 276 protected String defaultFromAddress; 277 278 protected List usedSources = new ArrayList (); 279 280 283 public SendMailTransformer() { 284 this.defaultNamespaceURI = NAMESPACE; 285 } 286 287 290 public void configure(Configuration configuration) 291 throws ConfigurationException { 292 super.configure(configuration); 293 this.defaultSmtpHost = configuration.getChild("smtphost").getValue(""); 294 this.defaultSmtpPort = configuration.getChild("smtpport").getValueAsInteger(25); 295 this.defaultFromAddress = configuration.getChild("from").getValue(""); 296 } 297 298 301 public void setup(SourceResolver resolver, Map objectModel, String src, 302 Parameters par) 303 throws ProcessingException, SAXException , IOException { 304 super.setup(resolver, objectModel, src, par); 305 306 this.mailHost = par.getParameter(PARAM_SMTPHOST, this.defaultSmtpHost); 307 this.mailPort = par.getParameterAsInteger(PARAM_SMTPPORT, this.defaultSmtpPort); 308 this.fromAddress = par.getParameter(PARAM_FROM, this.defaultFromAddress); 309 this.port = this.request.getServerPort(); 310 this.contextPath = this.request.getContextPath(); 311 this.sendPartial = par.getParameterAsBoolean(PARAM_SENDPARTIAL, true); 312 313 if (getLogger().isDebugEnabled()) { 314 getLogger().debug("Using host " + mailHost + " on port " + mailPort + ", from address " + fromAddress); 315 } 316 317 this.attachments = new ArrayList (); 318 this.defaultToAddresses = new ArrayList (); 319 appendToAddress(this.defaultToAddresses, par.getParameter(PARAM_TO, "")); 320 this.defaultReplyToAddresses = new ArrayList (); 321 appendToAddress(this.defaultReplyToAddresses, par.getParameter(PARAM_REPLY_TO, "")); 322 323 this.subject = par.getParameter(PARAM_SUBJECT, null); 324 this.body = par.getParameter(PARAM_BODY, null); 325 } 326 327 330 public void startTransformingElement(String uri, String name, String raw, 331 Attributes attr) 332 throws SAXException { 333 if (name.equals(ELEMENT_SENDMAIL)) { 334 this.toAddresses = new ArrayList (this.defaultToAddresses); 336 this.replyToAddresses = new ArrayList (this.defaultReplyToAddresses); 337 this.attachments.clear(); 338 } else if (name.equals(ELEMENT_SMTPHOST)) { 339 startTextRecording(); 340 this.mode = MODE_SMTPHOST; 341 } else if (name.equals(ELEMENT_SMTPPORT)) { 342 this.startTextRecording(); 343 this.mode = MODE_SMTPPORT; 344 } else if (name.equals(ELEMENT_MAILFROM)) { 345 startTextRecording(); 346 this.mode = MODE_FROM; 347 } else if (name.equals(ELEMENT_MAILTO)) { 348 startTextRecording(); 349 this.mode = MODE_TO; 350 } else if (name.equals(ELEMENT_REPLYTO)) { 351 startTextRecording(); 352 this.mode = MODE_REPLY_TO; 353 } else if (name.equals(ELEMENT_MAILSUBJECT)) { 354 startTextRecording(); 355 this.mode = MODE_SUBJECT; 356 } else if (name.equals(ELEMENT_MAILBODY)) { 357 String strBody = attr.getValue("src"); 358 if (strBody != null) { 359 this.bodyURI = strBody; 360 } 361 362 String mType = attr.getValue("mime-type"); 363 if (mType != null) { 364 this.bodyMimeType = mType; 365 } else { 366 this.bodyMimeType = DEFAULT_BODY_MIMETYPE; 367 } 368 369 startTextRecording(); 370 this.mode = MODE_BODY; 371 } else if (name.equals(ELEMENT_ATTACHMENT)) { 372 this.attachmentDescriptor = new AttachmentDescriptor(attr.getValue("name"), 373 attr.getValue("mime-type"), 374 attr.getValue("src"), 375 attr.getValue("url")); 376 this.mode = MODE_ATTACHMENT; 377 } else if (name.equals(ELEMENT_ATTACHMENT_CONTENT)) { 378 startSerializedXMLRecording(new Properties ()); 379 this.mode = MODE_ATTACHMENT_CONTENT; 380 } else { 381 throw new SAXException ("Unknown element <" + name + ">"); 382 } 383 } 384 385 388 public void endTransformingElement(String uri, String name, String raw) 389 throws SAXException , ProcessingException { 390 if (name.equals(ELEMENT_SENDMAIL)) { 391 if (getLogger().isInfoEnabled()) { 392 getLogger().info("Mail Subject: " + this.subject + "\n" + 393 "Body: " + this.body); 394 } 395 sendMail(); 396 } else if (name.equals(ELEMENT_SMTPHOST) ) { 397 this.mailHost = endTextRecording(); 398 this.mode = MODE_NONE; 399 } else if (name.equals(ELEMENT_SMTPPORT) ) { 400 this.mailPort = Integer.parseInt(this.endTextRecording()); 401 this.mode = MODE_NONE; 402 } else if (name.equals(ELEMENT_MAILFROM)) { 403 this.fromAddress = endTextRecording(); 404 this.mode = MODE_NONE; 405 } else if (name.equals(ELEMENT_MAILTO)) { 406 this.toAddresses.add(endTextRecording()); 407 this.mode = MODE_NONE; 408 } else if (name.equals(ELEMENT_REPLYTO)) { 409 this.replyToAddresses.add(endTextRecording()); 410 this.mode = MODE_NONE; 411 } else if (name.equals(ELEMENT_MAILSUBJECT)) { 412 String strSubject = endTextRecording(); 413 if (strSubject != null) { 414 this.subject = strSubject; 415 } else { 416 getLogger().debug("Mail: No Subject"); 417 } 418 this.mode = MODE_NONE; 419 } else if (name.equals(ELEMENT_ATTACHMENT)) { 420 this.attachments.add(this.attachmentDescriptor.copy()); 421 this.attachmentDescriptor = null; 422 this.mode = MODE_NONE; 423 } else if (name.equals(ELEMENT_ATTACHMENT_CONTENT)) { 424 this.attachmentDescriptor.setContent(endSerializedXMLRecording()); 425 this.mode = MODE_NONE; 426 } else if (name.equals(ELEMENT_MAILBODY)) { 427 String strB = null; 428 try { 429 strB = endTextRecording(); 430 } catch (Exception e) { 431 if (getLogger().isDebugEnabled()) { 432 getLogger().debug("Mail: No Body as String in config-file available"); 433 } 434 } 435 if (strB != null) { 436 this.body = strB; 437 } 438 this.mode = MODE_NONE; 439 } else { 440 throw new SAXException ("Unknown element <" + name + ">"); 441 } 442 } 443 444 private static void appendToAddress(List addresses, String s) { 445 StringTokenizer t = new StringTokenizer (s.trim(), ";"); 446 while (t.hasMoreElements()) { 447 addresses.add(t.nextToken()); 448 } 449 } 450 451 454 private void sendMail() { 455 try { 456 Properties props = new Properties (); 457 props.put("mail.smtp.host", this.mailHost); 458 props.put("mail.smtp.port", String.valueOf(this.mailPort)); 459 460 if (this.subject == null) { 461 this.ignoreHooksCount++; 462 super.sendStartElementEventNS(ELEMENT_ERROR); 463 super.sendTextEvent("Subject not available - sending mail aborted"); 464 super.sendEndElementEventNS(ELEMENT_ERROR); 465 this.ignoreHooksCount--; 466 return; 467 } 468 469 if (this.body == null && this.bodyURI == null) { 470 this.ignoreHooksCount++; 471 super.sendStartElementEventNS(ELEMENT_ERROR); 472 super.sendTextEvent("Mailbody not available - sending mail aborted"); 473 super.sendEndElementEventNS(ELEMENT_ERROR); 474 this.ignoreHooksCount--; 475 return; 476 } 477 478 Session session = Session.getDefaultInstance(props, null); 479 Transport trans = session.getTransport("smtp"); 480 trans.connect(); 481 482 this.smtpMessage = setUpMessage(session); 483 484 this.ignoreHooksCount++; 485 super.sendStartElementEventNS(ELEMENT_RESULT); 486 487 if (this.sendPartial) { 488 for (int i = 0; i < this.toAddresses.size(); i++) { 489 List v = new ArrayList (1); 490 v.add(this.toAddresses.get(i)); 491 sendMail(v, trans); 492 } 493 } else { 494 sendMail(this.toAddresses, trans); 495 } 496 497 trans.close(); 498 super.sendEndElementEventNS(ELEMENT_RESULT); 499 this.ignoreHooksCount--; 500 } catch (Exception e) { 501 getLogger().error("Exception sending mail", e); 502 sendExceptionElement(e); 503 } 504 } 505 506 510 private void sendMail(List newAddresses, Transport trans) 511 throws Exception { 512 AddressHandler[] iA = new AddressHandler[newAddresses.size()]; 513 514 for (int i = 0; i < newAddresses.size(); i++) { 515 InternetAddress inA = new InternetAddress ((String ) newAddresses.get(i)); 516 iA[i] = new AddressHandler(inA); 517 } 518 519 try { 520 InternetAddress [] iaArr = SendMailTransformer.getAddresses(iA); 521 this.smtpMessage.setRecipients(Message.RecipientType.TO, iaArr); 522 trans.sendMessage(this.smtpMessage, iaArr); 523 } catch (SendFailedException e) { 524 getLogger().error("Exception during sending of mail", e); 525 526 Address [] adr = e.getInvalidAddresses(); 527 for (int isfEx = 0; isfEx < iA.length; isfEx++) { 528 String tmpAddress = iA[isfEx].getAddress().getAddress(); 529 for (int sei = 0; sei < adr.length; sei++) { 530 if (((InternetAddress ) adr[sei]).getAddress() 531 .equalsIgnoreCase(tmpAddress)) { 532 iA[isfEx].setSendMailResult("Invalid address"); 533 } 534 } 535 } 536 537 Address [] ad = e.getValidUnsentAddresses(); 538 for (int isfEx = 0; isfEx < iA.length; isfEx++) { 539 String tmpAddress = iA[isfEx].getAddress().getAddress(); 540 for (int sei = 0; sei < ad.length; sei++) { 541 if (((InternetAddress ) ad[sei]).getAddress() 542 .equalsIgnoreCase(tmpAddress)) { 543 iA[isfEx].setSendMailResult("Recipient not found"); 544 } 545 } 546 } 547 } catch (Exception e) { 548 getLogger().error("Exception sending mail", e); 549 sendExceptionElement(e); 550 return; 551 } 552 553 generateSAXReportStatements(iA); 554 } 555 556 private Message setUpMessage(Session session) throws Exception { 557 Message sm = new MimeMessage (session); 558 559 Address [] replyTo = new Address [this.replyToAddresses.size()]; 561 for (int i = 0 ; i < this.replyToAddresses.size(); i++) { 562 replyTo[i] = new InternetAddress ((String ) this.replyToAddresses.get(i)); 563 } 564 sm.setReplyTo(replyTo); 565 sm.setFrom(new InternetAddress (this.fromAddress)); 566 sm.setSubject(this.subject); 567 568 BodyPart messageBodyPart = new MimeBodyPart (); 570 571 if (this.bodyURI != null) { 574 Source inSrc = resolver.resolveURI(this.bodyURI); 575 this.usedSources.add(inSrc); 576 InputStream inStr = inSrc.getInputStream(); 577 byte[] byteArr = new byte[inStr.available()]; 578 inStr.read(byteArr); 579 580 String mailBody = new String (byteArr); 581 messageBodyPart.setContent(mailBody, this.bodyMimeType); 582 } else { 583 messageBodyPart.setContent(this.body, this.bodyMimeType); 584 } 585 586 Multipart multipart = new MimeMultipart (); 587 multipart.addBodyPart(messageBodyPart); 588 589 Iterator iterAtt = this.attachments.iterator(); 591 592 while (iterAtt.hasNext()) { 593 AttachmentDescriptor aD = (AttachmentDescriptor) iterAtt.next(); 594 messageBodyPart = new MimeBodyPart (); 595 596 if (!aD.isTextContent()) { 597 Source inputSource = null; 598 DataSource dataSource = null; 599 600 inputSource = resolver.resolveURI(aD.isURLSource() ? aD.strAttrSrc : aD.strAttrFile); 601 this.usedSources.add(inputSource); 602 603 dataSource = new SourceDataSource(inputSource, aD.strAttrMimeType, aD.strAttrName); 604 605 messageBodyPart.setDataHandler(new DataHandler (dataSource)); 606 } else { 607 messageBodyPart.setContent(aD.strContent, aD.strAttrMimeType); 608 } 609 610 messageBodyPart.setFileName(aD.strAttrName); 611 multipart.addBodyPart(messageBodyPart); 612 } 613 614 sm.setContent(multipart); 615 616 sm.saveChanges(); 618 619 return sm; 620 } 621 622 private void generateSAXReportStatements(AddressHandler[] addressArr) 623 throws SAXException { 624 AttributesImpl impl = new AttributesImpl (); 625 626 for (int i = 0; i < addressArr.length; i++) { 627 String tmpAddress = addressArr[i].getAddress().getAddress(); 628 629 if (addressArr[i].getSendMailResult() == null) { 630 impl.addAttribute("", "to", "to", 631 "CDATA", tmpAddress); 632 super.sendStartElementEventNS(ELEMENT_SUCCESS, impl); 633 super.sendTextEvent("Mail sent"); 634 super.sendEndElementEventNS(ELEMENT_SUCCESS); 635 } else { 636 impl.addAttribute("", "to", "to", 637 "CDATA", tmpAddress); 638 super.sendStartElementEventNS(ELEMENT_FAILURE, impl); 639 super.sendTextEvent(addressArr[i].getSendMailResult()); 640 super.sendEndElementEventNS(ELEMENT_FAILURE); 641 } 642 } 643 } 644 645 private void sendExceptionElement(Exception ex) { 646 try { 647 this.ignoreHooksCount++; 648 super.sendStartElementEventNS("exception"); 649 super.sendStartElementEventNS("message"); 650 super.sendTextEvent(ex.getMessage()); 651 super.sendEndElementEventNS("message"); 652 653 662 663 super.sendEndElementEventNS("exception"); 664 this.ignoreHooksCount--; 665 } catch (SAXException e) { 666 getLogger().error("Error while sending a SAX-Event", e); 667 } 668 } 669 670 public static InternetAddress [] getAddresses(AddressHandler[] handlerArr) { 671 InternetAddress [] iaArr = new InternetAddress [handlerArr.length]; 672 673 for (int i = 0; i < handlerArr.length; i++) { 674 iaArr[i] = handlerArr[i].getAddress(); 675 } 676 677 return iaArr; 678 } 679 680 683 public void recycle() { 684 this.toAddresses = null; 685 this.defaultToAddresses = null; 686 this.attachments = null; 687 this.subject = null; 688 this.body = null; 689 this.bodyURI = null; 690 this.mailHost = null; 691 this.mailPort = 0; 692 this.fromAddress = null; 693 this.attachmentDescriptor = null; 694 this.port = 0; 695 this.contextPath = null; 696 this.sendPartial = true; 697 this.smtpMessage = null; 698 final Iterator i = this.usedSources.iterator(); 699 while ( i.hasNext() ) { 700 final Source source = (Source )i.next(); 701 this.resolver.release(source); 702 } 703 this.usedSources.clear(); 704 super.recycle(); 705 } 706 707 static class AttachmentDescriptor { 708 String strAttrName; 709 String strAttrMimeType; 710 String strAttrSrc; 711 String strAttrFile; 712 String strContent; 713 714 protected AttachmentDescriptor(String newAttrName, 715 String newAttrMimeType, 716 String newAttrSrc, String newAttrFile) { 717 this.strAttrName = newAttrName; 718 this.strAttrMimeType = newAttrMimeType; 719 this.strAttrSrc = newAttrSrc; 720 this.strAttrFile = newAttrFile; 721 } 722 723 protected void setContent(String newContent) { 724 this.strContent = newContent; 725 } 726 727 protected AttachmentDescriptor copy() { 728 AttachmentDescriptor aD = new AttachmentDescriptor(this.strAttrName, 729 this.strAttrMimeType, 730 this.strAttrSrc, 731 this.strAttrFile); 732 aD.setContent(this.strContent); 733 734 return aD; 735 } 736 737 protected boolean isURLSource() { 738 return (this.strAttrSrc != null); 739 } 740 741 protected boolean isFileSource() { 742 return (this.strAttrFile != null); 743 } 744 745 protected boolean isTextContent() { 746 return (this.strContent != null); 747 } 748 } 749 750 static class AddressHandler { 751 private InternetAddress address; 752 private String sendMailResult; 753 754 protected AddressHandler(InternetAddress newAddress) { 755 this.address = newAddress; 756 } 757 758 protected void setSendMailResult(String newSendMailResult) { 759 this.sendMailResult = newSendMailResult; 760 } 761 762 765 public InternetAddress getAddress() { 766 return address; 767 } 768 769 772 public String getSendMailResult() { 773 return sendMailResult; 774 } 775 } 776 } 777 | Popular Tags |