シリーズ:さくらサーバーで遊ぶ | |
---|---|
設定等のメモ | http://qiita.com/cielavenir/items/67a7c631713b4c816b6d |
SSL化 | http://qiita.com/cielavenir/items/aad29b2348fc4d3f9155 |
(さくらサーバー導入の主目的) メールをフックする |
http://qiita.com/cielavenir/items/5ce4568fc405329d421a |
Linuxbrewを導入する(旧) | http://qiita.com/cielavenir/items/741921fcecb281555f77 |
Homebrewを導入する | https://qiita.com/cielavenir/items/67ce0ec9cd8d43ed00f1 |
さくらサーバーでgccへの依存が発生しない理由 (HOMEBREW_BUILD_FROM_SOURCE=1は不要) |
https://qiita.com/cielavenir/items/410ded536d9b520989c4 |
用途が非常にspecificですが、汎用的に使える部分もあるのではないかと。
確認用に自分のtwitterにDMする機能も。
メールフィルタ
- qmailの場合
- USER-SUFFIX@DOMAIN
- ~/.qmail-SUFFIX
|/usr/bin/spamfilter
|$HOME/mailhook.rb
- maildropの場合(さくらサーバー)
- MAIL_ACCOUNT@DOMAIN
- ~/MailBox/MAIL_ACCOUNT/.mailfilter
- 実行時ディレクトリが.mailfilterと同じディレクトリになるようなので注意。
xfilter "/usr/local/bin/spamc"
to "|/home/USER/mailhook.rb"
フックスクリプト
mail gemとtwitter gemが必要です。
mailhook.rb
#!/usr/bin/env /home/cielavenir/.rbenv/shims/ruby
#coding:utf-8
#shebangが効かない場合のみenvを経由(さくらでは効きませんでした)
#mailhookとwebhook(cgi)を別のサーバーに置く場合はコメントアウト
$stdout=File.open('mailhook.log','w')
$stderr=$stdout
def msg(str)
$stdout.puts "Content-Type: text/plain\n\n"+str
end
#msg("Debug mode.\n"+RUBY_VERSION)
begin
Encoding.default_external='UTF-8'
require 'nkf'
require 'net/smtp'
require 'time'
require 'stringio'
require 'etc'
USERDIR=Etc.getpwuid.dir
#USERDIR='/home/USER'
#ENV['GEM_HOME']=USERDIR+'/.gem/ruby/2.2.0'
#変わったサーバーだとsshとcgiでUSERDIRの値が違うことがあるので確認
#system('stat '+USERDIR)
require 'mail'
#require 'git'
require 'twitter'
### config ###
#BLOB_NAME_FILE='https://github.com/USER/REPO/blob/__REVISION__/
#BLOB_NAME_FILE='https://gitlab.com/USER/REPO/blob/__REVISION__/
#BLOB_NAME_FILE='https://bitbucket.org/USER/REPO/src/__REVISION__/'
#BLOB_NAME_DIR='https://github.com/USER/REPO/tree/__REVISION__/
#BLOB_NAME_DIR='https://gitlab.com/USER/REPO/tree/__REVISION__/
#BLOB_NAME_DIR='https://bitbucket.org/USER/REPO/src/__REVISION__/'
REPO_LOCAL=USERDIR+'/git_dir'
MAIL_HOOKER=''
#Your primary email
SENDER_ADDR=''
SENDER_NAME=''
#Server info
SERVER_HOST='example.jp'
SERVER_PORT='587'
#nil/:enable_ssl/:enable_starttls
#enable_sslとenable_starttlsはどちらか片方しか対応していない場合がありますので注意して下さい。サーバーの設定を熟読。
SMTP_SSL_METHOD=:enable_starttls
#Envelope from (ドメインは基本的にSMTPサーバーと同じである必要があります)
SMTP_FROM=''
SMTP_PASSWORD=''
#OpenSSL::SSL::VERIFY_NONE/OpenSSL::SSL::VERIFY_PEER
SSL_VERIFY=OpenSSL::SSL::VERIFY_PEER
#CAファイル(問題がなければnilでも可)
SSL_CA='/usr/local/share/certs/ca-root-nss.crt'
HEADER=<<EOM
THIS_IS_HEADER
EOM
FOOTER=<<EOM
THIS_IS_FOOTER
EOM
MY_TWITTER='user'
client=Twitter::REST::Client.new{|config|
config.consumer_key = ""
config.consumer_secret = ""
config.access_token = ""
config.access_token_secret = ""
}
def predicate(name,*args)
#nameがargsに合致するか
args.any?{|arg|name.include?(arg)}
end
def predicate_subject(subject)
#なんでもかんでも転送すると問題があるので、特定の件名を持つメールにのみ反応する
!subject.start_with?('Re:') && !subject.start_with?('RE:')
end
def make_arg(body)
arg=[]
body.each_line{|line|
if line=~/(\d+)/
arg<<$1
end
}
arg
end
### end of config ###
#twitter gemはSSLContextを触れない
OpenSSL::SSL.module_eval{
remove_const(:VERIFY_PEER)
const_set( :VERIFY_PEER, SSL_VERIFY )
}
ENV['SSL_CERT_FILE']||=SSL_CA
def do_send(query)
boundary="webhook_scripter_boundary"
subject_mime=NKF.nkf("-wM",query[:subject])
begin
smtp=Net::SMTP.new(SERVER_HOST,SERVER_PORT)
if SMTP_SSL_METHOD
ctx = smtp.send(SMTP_SSL_METHOD)
#ctx.verify_mode = SSL_VERIFY
#ctx.ca_file = SSL_CA if SSL_CA
end
smtp.start('localhost',SMTP_FROM,SMTP_PASSWORD,:plain){
body = <<EOM
From: #{NKF.nkf("-wM",SENDER_NAME)} <#{SENDER_ADDR}>
To: #{query[:to]}
CC: #{query[:cc]*', '}
Sender: #{SMTP_FROM}
Subject: #{subject_mime.chomp}
In-Reply-To: <#{query[:msgid]}>
Date: #{Time.now.rfc2822}
MIME-Version: 1.0
X-Mailer: webhook_scripter
Content-Type: multipart/mixed; boundary="#{boundary}"
--#{boundary}
Content-Type: text/plain; charset=UTF-8
#{query[:body]}
EOM
smtp.send_mail body+"--#{boundary}--", SMTP_FROM, query[:to], *query[:cc]
true
}
rescue => err
puts err.inspect
false
end
end
def do_git(body)
pwd=Dir.pwd
Dir.chdir(REPO_LOCAL)
arg=make_arg(body)
#g=Git.open('.')
system("git pull origin master")
#g.pull('origin','master')
revision=`git rev-parse HEAD`.strip
#revision=g.object('HEAD').sha
BLOB_NAME_FILE.sub!('__REVISION__',revision)
BLOB_NAME_DIR.sub!('__REVISION__',revision)
body=StringIO.new
body.puts HEADER
body.puts
Dir.open('.'){|dir|
#せっかくpredicateはargを複数受け取れるけど、ファイル名がargでソートされてほしいので
arg.each{|ar|
dir.each{|name|
if predicate(name,ar)
body.puts BLOB_NAME_FILE+name if FileTest.file?(name)
body.puts BLOB_NAME_DIR+name if FileTest.directory?(name)
end
}
}
}
body.puts FOOTER
Dir.chdir(pwd)
body
end
### main
mail_str=STDIN.read
File.write('mailhook.eml',mail_str)
mail=Mail.read_from_string(mail_str) # eml given by mailhook
mail_body=mail.body.decoded.encode('UTF-8',mail.charset)
subject=mail.subject||''
if !predicate_subject(subject)
msg("email subject is not correct.")
exit
end
body=do_git(mail_body)
mail_from=mail.from
query={
:to => mail_from.shift,
:cc => ( mail_from+mail.to+(mail.cc||[]) )-[MAIL_HOOKER,SENDER_ADDR]+[SENDER_ADDR],
:subject => 'Re: '+(mail.subject||''),
:msgid => mail.message_id,
:body => body.string,
}
do_send(query)
client.create_direct_message(MY_TWITTER, "[mailhook performed]\n"+query[:body])
###
msg("Performed successfully.")
rescue => e
msg(e.to_s)
client.create_direct_message(MY_TWITTER, "[mailhook error]\n"+e.to_s)
end
メールフックサーバーを別の場所に
都合によりメールフックサーバーを別の場所に置く場合は、上のスクリプトをCGIとして設置した上で、以下のようなラッパーを用意します。
mailhook_wrapper.rb
#!/usr/bin/ruby
#coding:utf-8
require 'net/https'
require 'stringio'
http=Net::HTTP.new('SERVER',PORT)
http.use_ssl=true
#http.verify_mode=OpenSSL::SSL::VERIFY_NONE
s=StringIO.new;$stderr=s # stderrはフィルタの動作に邪魔なので潰す
DIR='/home/USER'
http.start{
mail_str=STDIN.read
File.open(DIR+'/webhook.eml','w'){|f|f.write mail_str}
resp=http.post('/cgi-bin/webhook.cgi',mail_str)
File.open(DIR+'/webhook.log','w'){|f|f.write resp.body}
}
最後に
メールフックの仕様上、認証を設けることは不可能です。いくら件名でフィルタを掛けているとは言え、使用の際は慎重を期して下さい。