HOME | 備忘録 | 書き込む |
サーバー上で、MTA が受け取ったメールをメール振り分けソフトに渡してメールを振り分けます。また、その際に スパムメールを判定することができます。サーバー上でスパムメールを学習させることによって学習する場所を1つにすることができ、 学習効率が向上します。
振り分けソフトには maildrop を、スパム判定には bogofilter を使います。 また、日本語のスパムメール判定を補助するために nkf, kakasi を使います。
念のため紫藤の環境を書いておきます。
ディストリビューション | Debian/GNU Linux, sarge |
MTA | postfix |
MDA | courier-imap |
保存形式 | Maildir |
参考にしたサイトは以下の通りです。
紫藤は apt-get install しました。 また、ソースは http://www.courier-mta.org/maildrop/ にあります。 Courier をソースからインストールした場合はすでにインストールされています。
ソースは http://bogofilter.sourceforge.net/
にあります。tar ball を展開して、
./configure → make → sudo make install
でインストールできます。
ソースは http://sourceforge.jp/projects/nkf/ からダウンロードできます。
展開 → ./configure → make → sudo make install
でインストールできます。
ディレクトリ | 説明 |
---|---|
.Business | 仕事関係の正常メール |
.Private | 私用の正常メール |
.Others | それ以外の正常メール |
.Spam | スパムメール |
.Business0 | スパムメールと誤判定された仕事用メール |
.Private0 | スパムメールと誤判定された私用メール |
.Others0 | スパムメールと誤判定されたその他のメール |
.Spam0 | 正常メールと誤判定されたスパムメール |
初期状態では .Spam にスパムメール、.Business, .Private, および .Others に正常メールがあるとします。 次のコマンドで学習させることができます。
$ find ~/Maildir/.Spam/cur -name "*" -type f -exec nkf -Z -m -j {} \; | kakasi -w | bogofilter -s $ find ~/Maildir/.Business/cur -name "*" -type f -exec nkf -Z -m -j {} \; | kakasi -w | bogofilter -n $ find ~/Maildir/.Private/cur -name "*" -type f -exec nkf -Z -m -j {} \; | kakasi -w | bogofilter -n $ find ~/Maildir/.Others/cur -name "*" -type f -exec nkf -Z -m -j {} \; | kakasi -w | bogofilter -n
.Spam0 にあるメールをスパムとして学習し、 .Spam に移動します。 また、.Business0, .Private0, および .Others0 にあるメールを正常メールとして学習し、 それぞれ .Business, .Private, および .Other に移動します。
bogofiliter の -Ns オプションは正常メールであることを取り消してスパムとして登録すること、また、 -Sn オプションはスパムであることを取り消して正常メールとして登録することを意味します。
01: #! /usr/bin/env python 02: """ 03: learing spam and nonspam mails automatically using with cron 04: """ 05: 06: import os, os.path 07: 08: USER="YourUserName" 09: MAILDIR=os.path.join("/home", USER, "Maildir") 10: BOGOFILTER="/usr/local/bin/bogofilter -B " 11: MV="/bin/mv " 12: CHECKDIRS=[ 13: (".Spam0", ".Spam", "-Ns"), 14: (".Business0", ".Business", "-Sn"), 15: (".Private0", ".Private", "-Sn"), 16: (".Others0", ".Others", "-Sn") 17: ] 18: 19: def su_c(usr, cmd): 20: return "su - " + usr + " -c \"" + cmd + "\"" 21: 22: def bogo(dir, flag): 23: return BOGOFILTER + flag + " " + dir 24: 25: def mv_all(dir0, dir1): 26: return MV + os.path.join(dir0, "*") + " " + dir1 27: 28: def reg_mail(dir0, dir1, flag): 29: dir0 = os.path.join(MAILDIR, dir0, "cur") 30: if os.listdir(dir0): 31: dir1 = os.path.join(MAILDIR, dir1, "cur") 32: os.system(su_c(USER, bogo(dir0, flag))) 33: os.system(su_c(USER, mv_all(dir0, dir1))) 34: 35: if __name__=='__main__': 36: for dir0, dir1, flag in CHECKDIRS: 37: reg_mail(dir0, dir1, flag)
"|/usr/bin/maildrop -d YourUserName"
01: MAILDIR="/home/YourUserName/Maildir/" 02: DEFAULT=$MAILDIR 03: NKF="/usr/local/bin/nkf" 04: NKF_OPTION="-Z -m -j" 05: WAKATI="/usr/local/bin/kakasi -w" 06: BOGOFILTER="/usr/local/bin/bogofilter" 07: BOGOFILTER_OPTION="-u -e -p" 08: BOGOJP="${NKF} ${NKF_OPTION} | ${WAKATI} | ${BOGOFILTER} ${BOGOFILTER_OPTION}" 09: 10: xfilter "${BOGOJP}" 11: 12: if (/^X-Bogosity: Spam, tests=bogofilter/) 13: { 14: to "${MAILDIR}.Spam/" 15: } 16: if (/^From:.*@mycompany\.co\.jp/:h) 17: { 18: to "${MAILDIR}.Business/" 19: } 20: 21: if (/^From:.*myfriend@/:h) 22: { 23: to "${MAILDIR}.Private/" 24: } 25: 26: if (/^From:.*sale/:h) 27: { 28: to "${MAILDIR}.Others/" 29: }if に続けて振り分けルールを正規表現で書きます。:h はヘッダーだけ調べるという意味です。 if(...) のあと改行して { を書かないと文法エラーになります。
bogofilter に '-u -e -p' オプションをつけるとスパムだと判定された場合
X-Bogosity: Spam, tests=bogofilter
という行が挿入されるので、それを振り分けルールに加えてスパムを振り分けます。
-u, -e, -p オプションはそれぞれ、判定後メールを登録する、エラーが起きなければ OS に 0 を返す、
メッセージヘッダの最後に X-Bogosity ... の行を追加する、という意味です。
振り分けルールに合致しないメールは DEFAULT に振り分けられます。
01: #! /usr/bin/env python 02: 03: """ 04: changing .mailfilter from a cliant 05: The script takes mailfilter.inp as a input file 06: The format of the input file is: 07: --- 08: # input file for mailfilter.py to make a .mailfilter at your mail server 09: # regexp(TAB)directory 10: ^From:.*@mycompany\.co\.jp .Business 11: ^From:.*myfriend@ .Private 12: ^From:.*sale .Others 13: --- 14: """ 15: 16: import os, os.path, sys, socket 17: from ftplib import FTP 18: from cStringIO import StringIO 19: 20: DIR="C:\\doc\\misc\\" 21: INP=os.path.join(DIR, "mailfilter.inp") 22: OUT=os.path.join(DIR, ".mailfilter") 23: LOG=os.path.join(DIR, "mailfilter.log") 24: SERVER="your.mail.server" 25: USER="YourUserName" 26: PASSWD="YourPassword" 27: HEADER= \ 28: """MAILDIR=\"/home/takafumi/Maildir/\" 29: DEFAULT=$MAILDIR 30: NKF=\"/usr/local/bin/nkf\" 31: NKF_OPTION=\"-Z -m -j\" 32: WAKATI=\"/usr/local/bin/kakasi -w\" 33: BOGOFILTER=\"/usr/local/bin/bogofilter\" 34: BOGOFILTER_OPTION=\"-u -e -p\" 35: BOGOJP=\"${NKF} ${NKF_OPTION} | ${WAKATI} | ${BOGOFILTER} ${BOGOFILTER_OPTION}\" 36: 37: xfilter \"${BOGOJP}\" 38: 39: if (/^X-Bogosity: Spam, tests=bogofilter/) 40: { 41: to \"${MAILDIR}.Junk/\" 42: } 43: 44: """ 45: 46: def make_rule(line): 47: """making a rule for .mailfilter from a line of the input file""" 48: regexp, dir = tuple(line.split('\t', 2)) 49: return "if (/" + regexp + "/:h)\n{\nto \"${MAILDIR}" + dir + "/\"\n}\n\n" 50: 51: def create_mailfilter(nfin, nfout): 52: """creating a .mailfilter at client""" 53: fin=file(nfin) 54: fout=file(nfout, 'w') 55: fout.write(HEADER) 56: for line in fin: 57: line = line.rstrip('\n') 58: len(line)==0 or \ 59: line[0]=='#' or \ 60: fout.write(make_rule(line)) 61: fin.close() 62: fout.close() 63: 64: def upload_file(server, user, passwd, fname, log_name): 65: """FTP upload a file to a server""" 66: try: 67: ftp = FTP(server) 68: except: 69: write_log(log_name, "Cannot connect to " + server, False) 70: 71: try: 72: ftp.login(user, passwd) 73: except: 74: ftp.close() 75: write_log(log_name, "Cannot login: " + server, False) 76: 77: put(ftp, fname, log_name) 78: ftp.quit() 79: write_log(log_name, "Upload " + fname + " done.", True) 80: 81: def put(ftp, fname, log_name): 82: """ put a file through ftp""" 83: try: 84: f = file(fname) 85: except: 86: ftp.close() 87: write_log(log_name, "Cannot open local file: " + fname, False) 88: try: 89: ftp.storlines("STOR " + os.path.basename(fname), f) 90: except: 91: ftp.close() 92: write_log(log_name, "Cannot Transfer local file: " + fname, False) 93: 94: f.close() 95: 96: def write_log(logfile, str, success): 97: """writing a log file""" 98: log = file(logfile, 'w') 99: log.write( ((not success) and 'Error: ' or '') + str + "\n") 100: log.close() 101: success or sys.exit(1) 102: 103: if __name__=='__main__': 104: create_mailfilter(INP, OUT) 105: upload_file(SERVER, USER, PASSWD, OUT, LOG)
ちなみに、公衆無線 LAN は不用心なので、出先からアクセスするときは メールの送受信には SSL (imap-ssl, smtp-ssl) と SMTP-AUTH を使っています。
今はサーバー証明書も安いので個人レベルでも利用できるようになりました。
HOME | 備忘録 | 書き込む |