« OSXクラッシュしまくり | Main | メモリ不具合疑惑 »

2007年3月 3日

ActionMailerでenvelope-fromを指定する

[ カテゴリ: Rails ]

メールを送信する場合、ヘッダに現れるFromのほかにenvelope-fromという「本当の」送信者をしています。これはSMTPでお話しするときに指定するので、実際にMUAでメールを読むときには、表面的には現れません。最終ホップ(すなわち、local mailerに落とすMTA)がReturn-Pathというヘッダ・フィールドを付加してenvelope-fromを保存するので、かろうじてこれでみることができます。

さて、envelope-fromがどうして大事かというと、エラーが発生したときに通知するアドレスとして利用されるからです(大昔はErrors-Toなんてヘッダ・フィールドを使ってたこともありましたが、さすがにもうないでしょう)。たとえば、メーリング・リストなんかでは、エラーが実際の送信者にいってもうれしくないですし、ましてやメーリング・リスト本体に送信されちゃっても困るわけで、envelope-fromとしてはメーリング・リストの管理者にしたりするわけですね。

ところで、ActionMailerですが。こいつでメールを送信するときのenvelope-fromは自由に設定できません。こりゃこまるだろーというのが今回の話。

たとえば以下のコード。

class TestMail < ActionMailer::Base
  def test1
    subject  "Test"
    from     "test-ml@example.com"
    body     "Please ignore."
  end
end

これでTestMail.deliver_test1とかやるとメールが飛んでいくわけですが、envelope-fromはもれなくtest-ml@example.comになります。もちろんこのままでいいときもあるわけですが、多くの場合、エラーは別途処理したいと思うので、異なるアドレスをenvelope-fromに指定したいでしょう。

というわけで、以下のパッチが役に立つかも?本家にもチケットあげたので、そのうち反映されるかもしれません…が、今まで投げつけたチケットはことごとく無視されてるか、すげー反応が遅いかって感じなので、期待薄かもしれない。

Index: lib/action_mailer/base.rb
===================================================================
--- lib/action_mailer/base.rb	(revision 6287)
+++ lib/action_mailer/base.rb	(working copy)
@@ -36,6 +36,7 @@
   # * <tt>from</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
   # * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
   # * <tt>bcc</tt> - Takes one or more email address. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc</tt> header.
+  # * <tt>sender</tt> - Takes just one email address.  This address will be used to envelope-from of SMTP and appear in <tt>Sender:</tt> header.
   # * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
   # * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
   # * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
@@ -315,6 +316,9 @@
     # header will be set by the delivery agent.
     adv_attr_accessor :sent_on
     
+    # Specify the Sender address for the message
+    adv_attr_accessor :sender
+    
     # Specify the subject of the message.
     adv_attr_accessor :subject
     
@@ -513,6 +517,7 @@
         m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
         m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
         m.cc  = quote_address_if_necessary(cc, charset) unless cc.nil?
+        m.sender = sender unless sender.nil?
 
         m.mime_version = mime_version unless mime_version.nil?
         m.date = sent_on.to_time rescue sent_on if sent_on
@@ -548,16 +553,19 @@
 
       def perform_delivery_smtp(mail)
         destinations = mail.destinations
+        sender = mail.sender(nil) || mail.from
         mail.ready_to_send
 
         Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain], 
             smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
-          smtp.sendmail(mail.encoded, mail.from, destinations)
+          smtp.sendmail(mail.encoded, sender, destinations)
         end
       end
 
       def perform_delivery_sendmail(mail)
-        IO.popen("#{sendmail_settings[:location]} #{sendmail_settings[:arguments]}","w+") do |sm|
+        arguments = sendmail_settings[:arguments].dup
+        arguments += " -f#{mail.sender(nil)}" if mail.sender(nil)
+        IO.popen("#{sendmail_settings[:location]} #{arguments}","w+") do |sm|
           sm.print(mail.encoded.gsub(/\r/, ''))
           sm.flush
         end

Comments

Post a comment




Remember Me?