Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for mail forwarding - recipients can now override to, cc, and bcc #52

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -179,6 +179,24 @@ its randomness and only customize the hostname.
:subject "Message IDs!"
:body "Regards."
:message-id #(postal.support/message-id "foo.bar.dom")}

#### Mail forwarding

If you're forwarding a mail it is possible to specify which recipients
will actually receive the mail regardless of what is given in the header:
Supply `:recipients` to override `:to`, `:cc`, and `:bcc`. The mail will
only be sent to those given in `:recipients` but to the recipients the mail
will appear to have been sent to those given in `:to` and `:cc`.

postal.core> (send-message {:from "foo@bar.dom"
:to "mailinglist@bar.dom"
:cc "another@another.dom"
:recipients ["member1@foo.dom" "member2@another.dom"]
:subject "An announcement to all members!"
:body "Regards."}

Note that some SMTP service providers - like Postmark - don't accept this and will
silently rewrite the message headers to match the given recipients.

#### User Agent

Expand Down Expand Up @@ -229,6 +247,7 @@ Paul Stadig
Phil Hagelberg
Roman Flammer
Sam Ritchie
�ystein Jakobsen

## License

Expand Down
20 changes: 10 additions & 10 deletions src/postal/message.clj
Expand Up @@ -34,11 +34,7 @@

(def default-charset "utf-8")

(declare make-jmessage)

(defn recipients [msg]
(let [^javax.mail.Message jmsg (make-jmessage msg)]
(map str (.getAllRecipients jmsg))))
(declare make-jmessage-with-recipients)

(defn sender [msg]
(or (:sender msg) (:from msg)))
Expand All @@ -64,7 +60,7 @@
(defn message->str [msg]
(with-open [out (java.io.ByteArrayOutputStream.)]
(let [^javax.mail.Message jmsg (if (instance? MimeMessage msg)
msg (make-jmessage msg))]
msg (:jmsg (make-jmessage-with-recipients msg)))]
(.writeTo jmsg out)
(str out))))

Expand Down Expand Up @@ -143,7 +139,7 @@
(proxy [javax.mail.Authenticator] []
(getPasswordAuthentication [] (PasswordAuthentication. user pass))))

(defn make-jmessage
(defn make-jmessage-with-recipients
([msg]
(let [{:keys [sender from]} msg
{:keys [user pass]} (meta msg)
Expand All @@ -152,11 +148,11 @@
(if user
(Session/getInstance props (make-auth user pass))
(Session/getInstance props)))]
(make-jmessage msg session)))
(make-jmessage-with-recipients msg session)))
([msg session]
(let [standard [:from :reply-to :to :cc :bcc
:date :subject :body :message-id
:user-agent]
:user-agent :recipients]
charset (or (:charset msg) default-charset)
jmsg (proxy [MimeMessage] [session]
(updateMessageID []
Expand All @@ -177,7 +173,11 @@
(.addHeader "User-Agent" (:user-agent msg (user-agent)))
(add-extra! (apply dissoc msg standard))
(add-body! (:body msg) charset)
(.saveChanges)))))
(.saveChanges))
(let [recipients (if (empty? (:recipients msg))
(.getAllRecipients jmsg)
(make-addresses (:recipients msg) charset))]
{:jmsg jmsg :recipients recipients}))))

(defn make-fixture [from to & {:keys [tag]}]
(let [uuid (str (UUID/randomUUID))
Expand Down
7 changes: 4 additions & 3 deletions src/postal/sendmail.clj
Expand Up @@ -22,7 +22,7 @@
;; OTHER DEALINGS IN THE SOFTWARE.

(ns postal.sendmail
(:use [postal.message :only [message->str sender recipients]]))
(:use [postal.message :only [message->str sender make-jmessage-with-recipients]]))

(def sendmails ["/usr/lib/sendmail"
"/usr/sbin/sendmail"
Expand Down Expand Up @@ -64,10 +64,11 @@
(.replaceAll text "\r\n" (System/getProperty "line.separator")))

(defn sendmail-send [msg]
(let [mail (sanitize (message->str msg))
(let [{:keys [jmsg recipients]} (make-jmessage-with-recipients msg)
mail (sanitize (message->str jmsg))
cmd (concat
[(sendmail-find) (format "-f %s" (sender msg))]
(recipients msg))
(map str recipients))
pb (ProcessBuilder. cmd)
p (.start pb)
smtp (java.io.PrintStream. (.getOutputStream p))]
Expand Down
29 changes: 20 additions & 9 deletions src/postal/smtp.clj
Expand Up @@ -22,25 +22,36 @@
;; OTHER DEALINGS IN THE SOFTWARE.

(ns postal.smtp
(:use [postal.message :only [make-jmessage]]
(:use [postal.message :only [make-jmessage-with-recipients make-addresses]]
[postal.support :only [make-props]])
(:import [javax.mail Transport Session]))
(:import [javax.mail Transport Session]
[javax.mail.internet InternetAddress MimeMessage]))

(defn ^:dynamic smtp-connect*
[^Transport transport ^String host ^String port ^String user ^String pass]
(.connect transport host port user pass))

(defn ^:dynamic smtp-send-single*
([^Transport transport ^MimeMessage jmsg recipients]
(.sendMessage transport jmsg recipients))
([^MimeMessage jmsg recipients]
(Transport/send jmsg recipients)))

(defn ^:dynamic smtp-send* [^Session session ^String proto
{:keys [host port user pass]} msgs]
(assert (or (and (nil? user) (nil? pass)) (and user pass)))
(with-open [transport (.getTransport session proto)]
(.connect transport host port user pass)
(let [jmsgs (map #(make-jmessage % session) msgs)]
(doseq [^javax.mail.Message jmsg jmsgs]
(.sendMessage transport jmsg (.getAllRecipients jmsg)))
{:code 0 :error :SUCCESS :message "messages sent"})))
(smtp-connect* transport host port user pass)
(doseq [msg msgs]
(let [{:keys [jmsg recipients]} (make-jmessage-with-recipients msg session)]
(smtp-send-single* transport jmsg recipients))))
{:code 0 :error :SUCCESS :message "messages sent"})

(defn smtp-send
([msg]
(let [jmsg (make-jmessage msg)]
(let [{:keys [jmsg recipients]} (make-jmessage-with-recipients msg)]
(try
(Transport/send jmsg)
(smtp-send-single* jmsg recipients)
{:code 0 :error :SUCCESS :message "message sent"}
(catch Exception e
{:code 99 :error (class e) :message (.getMessage e)}))))
Expand Down
26 changes: 26 additions & 0 deletions test/postal/test/message.clj
Expand Up @@ -225,3 +225,29 @@
:body "Where is that message ID!"
:user-agent "foo/1.0"})]
(is (.contains m "User-Agent: foo"))))

(deftest test-recipients
(let [r (map str (:recipients (make-jmessage-with-recipients
{:from "fee@bar.dom"
:to "Foo Bar <foo@bar.dom>"
:cc ["baz@bar.dom" "Quux <quux@bar.dom>"]
:subject "Test"
:body "Test!"
:charset "us-ascii"})))]
(is (.contains r "Foo Bar <foo@bar.dom>"))
(is (.contains r "baz@bar.dom"))
(is (.contains r "Quux <quux@bar.dom>"))))

(deftest test-recipients-with-recipients-given
(let [r (map str (:recipients (make-jmessage-with-recipients
{:from "fee@bar.dom"
:to "Foo Bar <foo@bar.dom>"
:cc ["baz@bar.dom" "Quux <quux@bar.dom>"]
:recipients ["recip1@r.dom" "Recip Two <recip2@r.dom>"]
:subject "Test"
:body "Test!"
:charset "us-ascii"})))]
(is (.contains r "recip1@r.dom"))
(is (.contains r "Recip Two <recip2@r.dom>"))
(is (not (.contains r "Foo Bar <foo@bar.dom>")))
(is (not (.contains r "Quux <quux@bar.dom>")))))
2 changes: 1 addition & 1 deletion test/postal/test/sendmail.clj
Expand Up @@ -22,4 +22,4 @@
;; OTHER DEALINGS IN THE SOFTWARE.

(ns postal.test.sendmail
(:use [postal.message :only [message->str sender recipients]]))
(:use [postal.message :only [message->str sender]]))
26 changes: 26 additions & 0 deletions test/postal/test/smtp.clj
Expand Up @@ -66,3 +66,29 @@
{"mail.smtp.port" 25
"mail.smtp.auth" "false"
"mail.smtp.host" "smtp.bar.dom"}))

(defn recipients [msg]
(let [capture (atom [])]
(binding [smtp/smtp-connect* (fn [& _])
smtp/smtp-send-single* (fn [transport msg recipients]
(reset! capture (mapv #(.getAddress %) recipients)))]
(smtp/smtp-send {} msg)
capture)))

(defmacro is-recipients [input want]
`(is (= (deref (recipients ~input)) ~want)))

(deftest t-recipients
(is-recipients {:from "foo@bar.dom"
:to "baz@bar.dom"
:cc ["foo@bar.dom"]
:subject "Test"
:body "Hello."}
["baz@bar.dom" "foo@bar.dom"])
(is-recipients {:from "foo@bar.dom"
:to "baz@bar.dom"
:cc ["foo@bar.dom"]
:recipients ["another@another.dom"]
:subject "Test"
:body "Hello."}
["another@another.dom"]))