drewr / postal

Clojure email support
MIT License
587 stars 85 forks source link

[PATCH] Support attachments from URLs #38

Closed ghost closed 10 years ago

ghost commented 11 years ago

Hello,

currently, it is only possible to add attachments from the file. However, sometimes it could be useful to attach files from classpath resources which might be within JAR files.

The patch below adds support for this.

From f11da6cd47fd9ce28cca3278f908f094499741bd Mon Sep 17 00:00:00 2001
From: Modestas Vainius <modestas@vainius.eu>
Date: Sun, 1 Dec 2013 20:51:54 +0200
Subject: [PATCH] Support attachments from URLs.

This commit adds support for adding attachments from URLs. Up until now
only files (and file: URLs) were supported. Paths without protocol
string are still assumed to be files on the local file system as before.

This is mainly for adding attachments from resources which might be
within JAR files (as returned by clojure.java.io/resource).

---
 src/postal/message.clj       | 15 ++++++++++++---
 test/postal/test/message.clj | 31 ++++++++++++++++++++++++++++++-
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/src/postal/message.clj b/src/postal/message.clj
index 929757f..7293222 100644
--- a/src/postal/message.clj
+++ b/src/postal/message.clj
@@ -23,10 +23,12 @@

 (ns postal.message
   (:use [clojure.set :only [difference]]
-        [clojure.java.io :only [file]]
+        [clojure.java.io :only [as-url as-file]]
         [postal.date :only [make-date]]
         [postal.support :only [do-when make-props message-id user-agent]])
   (:import [java.util UUID]
+           [java.net MalformedURLException]
+           [javax.activation DataHandler]
            [javax.mail Session Message$RecipientType]
            [javax.mail.internet MimeMessage InternetAddress
             AddressException]
@@ -61,6 +63,11 @@
     (into-array InternetAddress (map #(make-address % charset)
                                      addresses))))

+(defn- make-url [x]
+  (try (as-url x)
+       (catch MalformedURLException e
+         (as-url (as-file x)))))
+
 (defn message->str [msg]
   (with-open [out (java.io.ByteArrayOutputStream.)]
     (let [^javax.mail.Message jmsg (if (instance? MimeMessage msg)
@@ -97,8 +104,10 @@
 (defn eval-bodypart [part]
   (condp (fn [test type] (some #(= % type) test)) (:type part)
     [:inline :attachment]
-    (let [attachment-part (doto (javax.mail.internet.MimeBodyPart.)
-                            (.attachFile (file (:content part)))
+    (let [url (make-url (:content part))
+          attachment-part (doto (javax.mail.internet.MimeBodyPart.)
+                            (.setDataHandler (DataHandler. url))
+                            (.setFileName (re-find #"[^/]+$" (.getPath url)))
                             (.setDisposition (name (:type part))))]

       (when (:content-type part)
diff --git a/test/postal/test/message.clj b/test/postal/test/message.clj
index 64784c5..ef05240 100644
--- a/test/postal/test/message.clj
+++ b/test/postal/test/message.clj
@@ -24,11 +24,13 @@
 (ns postal.test.message
   (:use [postal.message]
         [clojure.test :only [run-tests deftest is]]
+        [clojure.java.io :as io]
         [postal.date :only [make-date]])
   (:import [java.util Properties UUID]
            [javax.mail Session Message$RecipientType]
            [javax.mail.internet MimeMessage InternetAddress
-            AddressException]))
+            AddressException]
+           [java.util.zip ZipOutputStream ZipEntry]))

 (deftest test-simple
   (let [m (message->str
@@ -81,8 +83,35 @@
                    {:type :attachment
                     :content f2}]})]
     (is (.contains m "tempfile"))
+    (is (.contains m (.getName f1)))
+    (is (.contains m "resolv.conf"))
+    (is (not (.contains m "etc")))
     (.delete f1)))

+(deftest test-attachments-from-url
+  (let [jar (doto (java.io.File/createTempFile "_postal-" ".jar"))
+        _ (with-open [zip-out (ZipOutputStream. (io/output-stream jar))
+                      zip-w (writer zip-out)]
+            (.putNextEntry zip-out (ZipEntry. "test-directory/test-filename.txt"))
+            (binding [*out* zip-w]
+              (println "tempfile contents")))
+        jar-url (str "jar:file:" (.getPath jar) "!/test-directory/test-filename.txt")
+        f-url "file:///etc/resolv.conf"
+        m (message->str
+            {:from "foo@bar.dom"
+             :to "baz@bar.dom"
+             :subject "Test"
+             :body [{:type :attachment
+                     :content jar-url}
+                    {:type :attachment
+                     :content f-url}]})]
+    (is (.contains m "tempfile"))
+    (is (.contains m "test-filename.txt"))
+    (is (not (.contains m "test-directory")))
+    (is (.contains m "resolv.conf"))
+    (is (not (.contains m "etc")))
+    (.delete jar)))
+
 (deftest test-attachment-with-custom-name-and-description
   (let [f1 (doto (java.io.File/createTempFile "_postal-" ".txt"))
         _ (doto (java.io.PrintWriter. f1)
-- 
1.8.5
drewr commented 11 years ago

Thanks for submitting this! Do you mind preparing a pull request so I can check it out in more detail?

ghost commented 10 years ago

There you go: https://github.com/drewr/postal/pull/41