Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@www.apache.org Received: (qmail 9201 invoked from network); 22 Feb 2007 23:35:50 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 22 Feb 2007 23:35:50 -0000 Received: (qmail 46298 invoked by uid 500); 22 Feb 2007 23:35:57 -0000 Delivered-To: apmail-jakarta-commons-dev-archive@jakarta.apache.org Received: (qmail 45766 invoked by uid 500); 22 Feb 2007 23:35:56 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 45755 invoked by uid 500); 22 Feb 2007 23:35:56 -0000 Received: (qmail 45749 invoked by uid 99); 22 Feb 2007 23:35:56 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 22 Feb 2007 15:35:56 -0800 X-ASF-Spam-Status: No, hits=-99.5 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 22 Feb 2007 15:35:47 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id DC8621A981A; Thu, 22 Feb 2007 15:35:26 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r510708 - in /jakarta/commons/proper/email/trunk/src: java/org/apache/commons/mail/HtmlEmail.java test/org/apache/commons/mail/HtmlEmailTest.java test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java Date: Thu, 22 Feb 2007 23:35:26 -0000 To: commons-cvs@jakarta.apache.org From: dion@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070222233526.DC8621A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: dion Date: Thu Feb 22 15:35:23 2007 New Revision: 510708 URL: http://svn.apache.org/viewvc?view=rev&rev=510708 Log: EMAIL-50 HTML Emails with images don't display in Outlook 2000 Modified: jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java Modified: jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java?view=diff&rev=510708&r1=510707&r2=510708 ============================================================================== --- jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java (original) +++ jakarta/commons/proper/email/trunk/src/java/org/apache/commons/mail/HtmlEmail.java Thu Feb 22 15:35:23 2007 @@ -20,9 +20,9 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; -import java.util.List; +import java.util.Map; import javax.activation.DataHandler; import javax.activation.URLDataSource; @@ -69,8 +69,11 @@ /** Html part of the message */ protected String html; - /** Embedded images */ - protected List inlineImages = new ArrayList(); + /** + * Embedded images Map where the key is the + * user-defined image name + */ + protected Map inlineImages = new HashMap(); /** HTML prefix and suffix for default HTML mail */ private static final String HTML_MESSAGE_START = "
";
@@ -162,14 +165,14 @@
      */
     public String embed(String url, String name) throws EmailException
     {
-    	try
-    	{
-    		return embed(new URL(url), name);
-    	}
-    	catch (MalformedURLException e)
-    	{
-    		throw new EmailException("Invalid URL", e);
-    	}
+        try
+        {
+            return embed(new URL(url), name);
+        }
+        catch (MalformedURLException e)
+        {
+            throw new EmailException("Invalid URL", e);
+        }
     }
 
     /**
@@ -179,7 +182,8 @@
      * the mail body.  It allows, for instance, to add inline images
      * to the email.  Inline files may be referenced with a
      * cid:xxxxxx URL, where xxxxxx is the Content-ID
-     * returned by the embed function.
+     * returned by the embed function. It is an error to bind the same name
+     * to more than one URL.
      *
      * 

Example of use:

      * HtmlEmail he = new HtmlEmail();
@@ -189,6 +193,13 @@
      * // code to set the others email fields (not shown)
      * 
* + *

+ * NOTE: Clients should take care to ensure that different URLs are bound to + * different names. This implementation tries to detect this and throw + * EmailException. However, it is not guaranteed to catch + * all cases, especially when the URL refers to a remote HTTP host that + * may be part of a virtual host cluster. + * * @param url The URL of the file. * @param name The name that will be set in the filename header * field. @@ -199,6 +210,31 @@ */ public String embed(URL url, String name) throws EmailException { + InlineImage ii = null; + + // check if the URL contents have already been attached; + // if so, return the cached CID value. + if (inlineImages.containsKey(name)) + { + ii = (InlineImage) inlineImages.get(name); + // make sure the supplied URL points to the same thing + // as the one already associated with this name. + if (url.equals(ii.getURL())) + { + return ii.getCid(); + } + else + { + throw new EmailException("embedded file name '" + name + + " is already bound to URL " + ii.getURL().toString() + + "; existing names cannot be rebound"); + } + // NOTE: Comparing URLs with URL.equals() is known to be + // inconsistent when dealing with virtual hosting over HTTP, + // but since these are almost always files on the local machine, + // using equals() should be sufficient. + } + // verify that the URL is valid try { @@ -218,8 +254,11 @@ mbp.setFileName(name); mbp.setDisposition("inline"); String cid = EmailUtils.randomAlphabetic(HtmlEmail.CID_LENGTH).toLowerCase(); - mbp.addHeader("Content-ID", "<" + cid + ">"); - this.inlineImages.add(mbp); + mbp.setContentID("<" + cid + ">"); + + ii = new InlineImage(cid, url, mbp); + this.inlineImages.put(name, ii); + return cid; } catch (MessagingException me) @@ -238,17 +277,7 @@ { try { - // if the email has attachments then the base type is mixed, - // otherwise it should be related - if (this.isBoolHasAttachments()) - { - this.buildAttachments(); - } - else - { - this.buildNoAttachments(); - } - + build(); } catch (MessagingException me) { @@ -261,22 +290,29 @@ * @throws EmailException EmailException * @throws MessagingException MessagingException */ - private void buildAttachments() throws MessagingException, EmailException + private void build() throws MessagingException, EmailException { MimeMultipart container = this.getContainer(); MimeMultipart subContainer = null; - MimeMultipart subContainerHTML = new MimeMultipart("related"); BodyPart msgHtml = null; BodyPart msgText = null; - container.setSubType("mixed"); + container.setSubType("related"); subContainer = new MimeMultipart("alternative"); if (EmailUtils.isNotEmpty(this.text)) { msgText = new MimeBodyPart(); - subContainer.addBodyPart(msgText); + if (this.inlineImages.size() > 0) + { + subContainer.addBodyPart(msgText); + } + else + { + container.addBodyPart(msgText); + } + // apply default charset if one has been set if (EmailUtils.isNotEmpty(this.charset)) { msgText.setContent( @@ -291,17 +327,17 @@ if (EmailUtils.isNotEmpty(this.html)) { + msgHtml = new MimeBodyPart(); if (this.inlineImages.size() > 0) { - msgHtml = new MimeBodyPart(); - subContainerHTML.addBodyPart(msgHtml); + subContainer.addBodyPart(msgHtml); } else { - msgHtml = new MimeBodyPart(); - subContainer.addBodyPart(msgHtml); + container.addBodyPart(msgHtml); } + // apply default charset if one has been set if (EmailUtils.isNotEmpty(this.charset)) { msgHtml.setContent( @@ -313,96 +349,91 @@ msgHtml.setContent(this.html, Email.TEXT_HTML); } - Iterator iter = this.inlineImages.iterator(); + Iterator iter = this.inlineImages.values().iterator(); while (iter.hasNext()) { - subContainerHTML.addBodyPart((BodyPart) iter.next()); + InlineImage ii = (InlineImage) iter.next(); + container.addBodyPart(ii.getMbp()); } } - // add sub containers to message - this.addPart(subContainer, 0); - if (this.inlineImages.size() > 0) { // add sub container to message - this.addPart(subContainerHTML, 1); + this.addPart(subContainer, 0); } } /** - * @throws EmailException EmailException - * @throws MessagingException MessagingException + * Private bean class that encapsulates data about URL contents + * that are embedded in the final email. + * @since 1.1 */ - private void buildNoAttachments() throws MessagingException, EmailException + private static class InlineImage { - MimeMultipart container = this.getContainer(); - MimeMultipart subContainerHTML = new MimeMultipart("related"); - - container.setSubType("alternative"); - - BodyPart msgText = null; - BodyPart msgHtml = null; - - if (EmailUtils.isNotEmpty(this.text)) + private String cid; + private URL url; + private MimeBodyPart mbp; + + /** + * Creates an InlineImage object to represent the + * specified content ID and MimeBodyPart. + * @param cid the generated content ID + * @param url the URL that points to the content + * @param mbp the MimeBodyPart that contains the encoded + * data + */ + public InlineImage(String cid, URL url, MimeBodyPart mbp) + { + this.cid = cid; + this.url = url; + this.mbp = mbp; + } + + /** + * Returns the unique content ID of this InlineImage. + * @return the unique content ID of this InlineImage + */ + public String getCid() { - msgText = this.getPrimaryBodyPart(); - if (EmailUtils.isNotEmpty(this.charset)) - { - msgText.setContent( - this.text, - Email.TEXT_PLAIN + "; charset=" + this.charset); - } - else - { - msgText.setContent(this.text, Email.TEXT_PLAIN); - } + return cid; } - if (EmailUtils.isNotEmpty(this.html)) + /** + * Returns the URL that points to the encoded content. + * @return the URL pointing to the encoded content + */ + public URL getURL() { + return url; + } + + /** + * Returns the MimeBodyPart that contains the + * encoded InlineImage data. + * @return the MimeBodyPart containing the encoded + * InlineImage data + */ + public MimeBodyPart getMbp() + { + return mbp; + } + + // equals()/hashCode() implementations, since this class + // is stored as a entry in a Map. + + public boolean equals(Object obj) + { + if (this == obj) return true; + if (!(obj instanceof InlineImage)) return false; + + InlineImage that = (InlineImage) obj; + + return this.cid.equals(that.cid); + } + + public int hashCode() { - // if the txt part of the message was null, then the html part - // will become the primary body part - if (msgText == null) - { - msgHtml = getPrimaryBodyPart(); - } - else - { - if (this.inlineImages.size() > 0) - { - msgHtml = new MimeBodyPart(); - subContainerHTML.addBodyPart(msgHtml); - } - else - { - msgHtml = new MimeBodyPart(); - container.addBodyPart(msgHtml, 1); - } - } - - if (EmailUtils.isNotEmpty(this.charset)) - { - msgHtml.setContent( - this.html, - Email.TEXT_HTML + "; charset=" + this.charset); - } - else - { - msgHtml.setContent(this.html, Email.TEXT_HTML); - } - - Iterator iter = this.inlineImages.iterator(); - while (iter.hasNext()) - { - subContainerHTML.addBodyPart((BodyPart) iter.next()); - } - - if (this.inlineImages.size() > 0) - { - // add sub container to message - this.addPart(subContainerHTML); - } + return cid.hashCode(); } } } Modified: jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java?view=diff&rev=510708&r1=510707&r2=510708 ============================================================================== --- jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java (original) +++ jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/HtmlEmailTest.java Thu Feb 22 15:35:23 2007 @@ -165,19 +165,44 @@ assertNotNull(strEmbed); assertEquals(HtmlEmail.CID_LENGTH, strEmbed.length()); + // if we embed the same name again, do we get the same content ID + // back? + String testCid = + this.email.embed(new URL(this.strTestURL), "Test name"); + assertEquals(strEmbed, testCid); + + // if we embed the same URL under a different name, is the content ID + // unique? + String newCid = + this.email.embed(new URL(this.strTestURL), "Test name 2"); + assertFalse(strEmbed.equals(newCid)); + // ==================================================================== // Test Exceptions // ==================================================================== - // bad URL + + // Does an invalid URL throw an exception? try { - this.email.embed(new URL("http://bad.url"), "Test name"); + this.email.embed(new URL("http://bad.url"), "Bad URL"); fail("Should have thrown an exception"); } catch (EmailException e) { - assertTrue(true); + // expected } + + // if we try to embed a different URL under a previously used name, + // does it complain? + try + { + this.email.embed(new URL("http://www.google.com"), "Test name"); + fail("shouldn't be able to use an existing name with a different URL!"); + } + catch (EmailException e) + { + // expected + } } /** Modified: jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java?view=diff&rev=510708&r1=510707&r2=510708 ============================================================================== --- jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java (original) +++ jakarta/commons/proper/email/trunk/src/test/org/apache/commons/mail/mocks/MockHtmlEmailConcrete.java Thu Feb 22 15:35:23 2007 @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.List; +import java.util.Map; import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; @@ -75,7 +76,7 @@ /** * @return inlineImages */ - public List getInlineImages() + public Map getInlineImages() { return inlineImages; } --------------------------------------------------------------------- To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-dev-help@jakarta.apache.org