PostfixとActionMailerでメール受信処理
postfixからメールの受信関係の処理をしました。 プログラム自体は凄く簡単なんですが、postfixの設定など含め色々つまづいたので、メモしておきます。 なお、サーバーはCentos6を使っています。
普通はRailsを使うと思いますが、単純にscriptを叩くだけだったのでファイル一枚を目指します。 今回の例では、メールを受信した後、外部のSSLがかかった、XMLRPCのAPIを叩きました。
まずは、何はともあれ、メールを送れるようにしないといけないと。 ということで、下記のように登録しました。
mail.hogehoge.com A 111.111.111.111 hogehoge.com MX mail.hogehoge.com 10
次に、postfixの設定をしましょう。 ポイントは、inet_interfacesをallにすることと、mydestinationを設定することです。 inet_interfacesをallにするとどこからでも受信できるようになります。 mydestinationはメールの最終到着地点を定義しています。 該当箇所のコメントアウトを外すだけだったりするので、下記は抜粋です。
$ sudo vi /etc/postfix/main.cf myhostname = margin-0.in inet_interfaces = all mydestination = $myhostname, localhost.$mydomain, localhost
今回は、メールを受信したら、BacklogというチケットシステムのAPIを叩いて、チケットを登録するスクリプとです。 このAPIはxml-rpcという規格で作られていたため、rubyの標準添付ライブラリのxmlrpcも利用しています。 また、SSLとBasic認証もかかっています。
1 #-*- encoding: utf-8 -*- 2 3 require 'rubygems' 4 require 'xmlrpc/client' 5 require 'action_mailer' 6 require 'kconv' 7 8 class NilClass 9 def blank?; true; end 10 end 11 12 class String 13 def blank?; self==""; end 14 end 15 16 class MailKicker < ActionMailer::Base 17 @@server = XMLRPC::Client.new("xxx.backlog.jp","/xmlrpc",443,nil,nil,"user","password",true,nil) 18 @@server.http_header_extra= {"Content-Type" => "text/xml; charset=utf-8"} 19 20 # 受信したらとりあえず登録する 21 # bundle exec ruby -r ./lib/mail_kicker.rb -e "MailKicker.receive(STDIN.read)" 22 def receive(email=nil) 23 puts email.inspect 24 email.subject = "#{Time.now}に登録したチケット" if email.subject.blank? 25 params = { 26 projectId: 32272, #26584, 27 summary: email.subject.encode("utf-8"), 28 description: email.body.to_s.toutf8 29 } 30 @@server.call("backlog.createIssue", params) 31 end 32 end
小さいプログラムですが、ポイントとしては下記です。
email.bodyはMail::Bodyオブジェクトです。
email.bodyのencodingがiso-2022-jpだったりするので、強制的にkconvでutf8にしています。
xmlrpc( http://xmlrpc.scripting.com/spec.html )がちょっと古そうな規格でした。xmlでのやりとりをするAPIです。rubyのライブラリのおかげで簡単に使えました。
ActionMailer::Baseの子クラスであるMailKickerで作っているreceiveはメソッド名もポイントになります。ActionMailer::Baseでクラスメソッドにreceiveが用意されており、インスタンスメソッドのreceiveが呼ばれるようになっています。また、receiveの引数に入るemailという変数はMailオブジェクトです。Mailオブジェクトは https://github.com/mikel/mail を参照下さい。
参考までに、ActionMailer::Base.receiveは下記に書いておきます。
このクラスメソッドのreceiveを外部コマンドから呼び出すことになります。
def receive(raw_mail) ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload| mail = Mail.new(raw_mail) set_payload_for_mail(payload, mail) new.receive(mail) end end
次の設定は、メールを受信したときにスクリプトを走らせるための設定です。 引き続き、main.cfの該当箇所を修正します。
$ sudo vi /etc/postfix/main.cf alias_maps = hash:/etc/postfix/aliases alias_database = hash:/etc/postfix/aliases
次は、今設定した/etc/postfix/aliasesを書きます。
$ sudo vi /etc/postfix/aliases hoge: "| SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt GEM_HOME=/usr/local/rvm/gems/ruby-1.9.3-p0 /usr/local/rvm/rubies/ruby-1.9.3-p0/bin/ruby -r /var/apps/alpha/kickers/lib/mail_kicker.rb -e 'MailKicker.receive(STDIN.read)' >> /tmp/posfix-test.log" fuga: "| echo fuga >/tmp/post-test.txt"
PATHが通っていないので、基本的にフルパスで書きます。
rubyの-eオプションでワンライナーでrubyを実行しています(rails2の場合はscript/runner、rails3の場合はrails runnerを使うでしょうが・・)。
rubyの-rオプション で、requrieするファイルを追加しています。
ActionMailer::Baseのクラスメソッドのreceiveを呼び出しています。
rubyでパイプで繋いだ標準入力を利用するために、STDIN.readを使っています。
内部で使っているrubygemsを指定するために、GEM_HOMEを指定しています。
SSL通信を行うため、SSL_CERT_FILEも指定しています。
これで、大体完了だったんですが、SELinuxのおかげでrubyの実行権限がないと言われてだいぶ嵌りました。 とりあえず、OFFにします。
最後に外部から、[email protected] にメールを送ってあげればOKです。 テストがしにくいので、mockやstubを使ってrspecでテストしてあげると良いですね。
https://github.com/rails/rails/tree/master/actionmailer
http://edgeguides.rubyonrails.org/action_mailer_basics.html
https://github.com/mikel/mail
http://xmlrpc.scripting.com/spec.html