コメントスパム対策系プラグインでトラックバックまでrejectされる問題
posted by jun-g at Mon, 18 Dec 2006 02:37 JST
こないだ書いたcheck_javascriptプラグインを使えばtrackbackプラグインへの修正は不要、っていうのは嘘でした。すみません。check_javascriptプラグインを導入すると、トラックバック受信時にエラーが発生するようになります。
どうもこの辺り、設計がいけてない気がする。ちゃんと仕組みを整理した上で修正したいので、ちょっとまとめてみる。
まず、そもそも「trackbackプラグインがcommentsプラグインの機能を使用する事を前提に実装されている」という事が問題だと考えられる。しかし、これを言い出すと、trackbackプラグインをスクラッチから書き直す事になってしまう。本来はその方が良いのかもしれないけど、ちょっと大変そうなので今回はパス。
次に、shunuhsさんがsh1.2 pyblosxom : 続: MagicWord/comments/trackback pluginで取った「トラックバック経由で受け取ったデータにtrackbackプラグイン内部で判定用パラメータを追加し、コメントスパム対策系プラグイン内でトラックバックか判定する」という対策の場合、使用するコメントスパム対策プラグイン全てに「受け取ったデータはトラックバックか?」という判定処理を加える必要があるし、既にPyblosxom Plugin Registryに登録されているコメントスパム対策系プラグインは、やはりそのままでは使えない事になる。できればこれらのプラグインを修正無しで使用できるようにしたい。
以上の事を(ほんのちょっとだけ)考えた結果、「cb_comment_reject関数はwriteComment()を呼び出す前、しかもtrackbackプラグインでは実行されない関数内で実行するようにすればいいのでは?」という結論に至った。
ということでcomments.pyを早速修正。svn diffの結果は以下の通り。
Index: comments.py =================================================================== --- comments.py (リビジョン 954) +++ comments.py (作業コピー) @@ -359,19 +359,6 @@ os.makedirs(cdir) cfn = os.path.join(cdir,entry['fn']+"-"+comment['pubDate']+"."+config['comment_draft_ext']) - - argdict = { "request": request, "comment": comment } - reject = tools.run_callback("comment_reject", - argdict, - donefunc=lambda x:x != 0) - - if (isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2: - reject_code, reject_message = reject - else: - reject_code, reject_message = reject, "Comment rejected." - - if reject_code == 1: - return reject_message def makeXMLField(name, field): return "<"+name+">" + cgi.escape(field.get(name, "")) + "</"+name+">\n"; @@ -475,13 +462,9 @@ comment_dir = os.path.join(config['comment_dir'], entry['absolute_path']) # create the message + from email.Header import Header from email.MIMEText import MIMEText message = [] - message.append("From: %s" % email) - message.append("To: %s" % config["comment_smtp_to"]) - message.append("Date: %s" % formatdate(float(comment['pubDate']))) - message.append("Subject: comment by %s" % author) - message.append("") message.append("Name: %s" % author) if comment.has_key('email'): message.append("Email: %s" % comment['email']) @@ -498,7 +481,13 @@ body = '\n'.join(message) body = MIMEText(body.encode('utf-8'), 'plain', 'utf-8') - + subj = "comment by %s" % author + subj = Header(subj.encode("utf-8"), "utf-8") + body["Subject"] = subj + body["From"] = config["comment_smtp_from"] + body["To"] = config["comment_smtp_to"] + body["Date"] = formatdate(float(comment['pubDate'])) + if (config.has_key('comment_mta_cmd')): argv = [config['comment_mta_cmd'], '-s', @@ -506,7 +495,7 @@ config['comment_smtp_to']] # TODO: switch to subprocess when we can require python 2.4 process = popen2.Popen3(argv, capturestderr=True) - process.tochild.write(body) + process.tochild.write(body.as_string()) process.tochild.close() process.wait() stdout = process.fromchild.read() @@ -521,7 +510,7 @@ server = smtplib.SMTP(config['comment_smtp_server']) server.sendmail(from_addr=email, to_addrs=config['comment_smtp_to'], - msg=body) + msg=body.as_string()) server.quit() except Exception, e: @@ -714,9 +703,22 @@ cdict['email'] = form['email'].value cdict['ipaddress'] = pyhttp.get('REMOTE_ADDR', '') - data["comment_message"] = writeComment(request, config, data, \ - cdict, encoding) + argdict = { "request": request, "comment": cdict } + reject = tools.run_callback("comment_reject", + argdict, + donefunc=lambda x:x != 0) + if (isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2: + reject_code, reject_message = reject + else: + reject_code, reject_message = reject, "Comment rejected." + if reject_code == 1: + data["comment_message"] = reject_message + else: + data["comment_message"] = writeComment(request, config, data, \ + cdict, encoding) + + def massage_link(linkstring): """Don't allow html in the link string. Prepend http:// if there isn't already a protocol."""
こないだの修正分も含まれているのでちょっと紛らわしいかも。この差分の内容には以下の修正が含まれている。
- cb_comment_rejectをwriteComments()実行前に処理するように変更
- コメント/トラックバック内容に非ASCII文字が含まれていた場合にメール送信時にエラーになる問題を修正
- 「From」「To」「Subject」「Date」がメールヘッダではなくメール本文に設定されてしまう不具合を修正
これで、スパム対策プラグインを導入しても、修正無しにトラックバックの受信が行えるようになるはず。
しかし、上記の修正を適用するとトラックバック受信時にcb_comment_reject関数が実行されなくなってしまう為、トラックバックスパム対策を行うフックが無くなってしまう。これは、trackbackプラグイン側にフックを用意すれば良いと思う。trackback.pyを以下のように修正してみた。
Index: trackback.py =================================================================== --- trackback.py (リビジョン 954) +++ trackback.py (作業コピー) @@ -96,6 +96,19 @@ 'link' : form['url'].value, \ 'source' : form.getvalue('blog_name', ''), \ 'description' : form.getvalue('excerpt', '') } + + argdict = { "request": request, "comment": cdict } + reject = tools.run_callback("trackback_reject", + argdict, + donefunc=lambda x:x != 0) + if (isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2: + reject_code, reject_message = reject + else: + reject_code, reject_message = reject, "Trackback rejected." + if reject_code == 1: + print >> response, tb_bad_response % reject_message + return 1 + from Pyblosxom.entries.fileentry import FileEntry from Pyblosxom.pyblosxom import Request from Pyblosxom.pyblosxom import PyBlosxom
この修正を適用すると、トラックバック処理時にcb_trackback_reject関数が実行されるようになる。トラックバックスパム対策には、この関数を持つプラグインを実装すれば良い。
コメントとトラックバックのスパム対策に同じ処理(IPアドレスチェックとかNGワードチェックとか)を使用したい場合は、
def cb_comment_reject(): spam_check() def cb_trackback_reject(): spam_check() def spam_check(): (処理)
みたいなプラグインの実装にすれば良いと思う。どうせこれまでに存在したコメントスパム対策プラグインはトラックバックに対しては機能しなかったはずなので、この仕組みで問題ないはず。
と、まぁ自分なりの修正案をガーっと書いてみた訳やけど、実はあんまり動作確認もしてないし、Pyblosxom自体の仕組みもソースを斜め読みした程度しか理解してないので、もしかしたらデタラメな事書いてるかも。ツッコミ等ありましたらよろしくお願いします。> pyblosxomユーザーの皆様
とりあえず自分のところには、上記修正を適用したcommentsプラグインを導入してみて、問題無さそうならパッチをMLに投げてみる事にしよう。trackbackプラグインの修正は…もうちょっと考えてみよう。