daemonfreaks.com

github actionsでデプロイ

posted by jun-g at Sun, 13 Feb 2022 12:55 JST

前回 からの続きで、新しい記事を書いてgithubにpushしたらgithub actionsを使ってサーバーに自動的にデプロイされるようにしました。

尚、実装には このページ を参考にしました。分かりやすい記事を書いていただきありがとうございました。

どこからでも記事が書けるように、という目標でチマチマ整備を進めてたのですが、これでgithubにログインできればどこからでも記事を書けるようにはなったので、一旦目標達成ということで。

mtimeをメタデータから取る

posted by jun-g at Sun, 06 Feb 2022 23:40 JST

PyBlosxomは記事の作成日時を各エントリーファイルの修正日時(mtime)から取得します。投稿後に過去記事を修正した場合に作成日付が変わってしまうのが気に入らなかったので、entrycacheというプラグインを使用して初回に記事が読み込まれた際のmtimeがキャッシュされるようにしていましたが、キャッシュファイルがサーバー側で生成・更新され続けるため、キャッシュファイルを紛失してしまうと作成日時が再現できなくなる脆弱な仕組みが気になっていたので、 entrymtime というプラグインを自作し、各エントリーファイルのmetadataに作成日時を書いておけるようにしました。

移行の為にentrycacheのキャッシュファイルから作成日時を抽出して各エントリーファイルのmetadataに追記。

import os, time

with open('.entrycache') as c:
    data = c.read()

caches = eval(data)

for item in caches.items():
    fname = os.path.basename(item[0])
    if not os.path.exists(fname):
        continue
    t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item[1]))
    with open(fname) as r:
        lines = r.readlines()
    lines.insert(1, "#entrymtime %s\n" % t)
    with open(os.path.join("new", fname), 'w') as w:
        w.writelines(lines)

ついでに拡張子がtxtのエントリーファイルの拡張子を blx に変更しました。

さらについでに、エントリーファイルもgithubで管理するようにしました。ついでというよりこれが本当のやりたかったこと。

うーん、2022年の記事とは思えないですね…w

サーバーのPythonバージョンを3.8に上げたら その2

posted by jun-g at Sun, 12 Sep 2021 02:59 JST

直りました。

  1. commentsプラグインで他にcgi.parseを使ってるところがあったのでhtml.parseに変更してエラーが出ないようにした。
  2. python3ブランチでは、HTMLで書いたファイルは拡張子をtxtからblxに変えないとダメな変更が入っていて、それに気付いてなくてPython3に移行した後ずっと発生してた問題だった。一旦拡張子txtのまま読み込むようにした。

ほったらかしだと気持ち悪かったので直せてよかったです。

サーバーのPythonバージョンを3.8に上げたら

posted by jun-g at Mon, 06 Sep 2021 00:14 JST

  1. commentsプラグインでエラーが出るようになったので直した。 cgiモジュールからparse_qs()が削除された ことによるもの。代わりにurllib.parse.parse_qs()を使えばOK。
  2. HTMLで書いた、拡張子txtのエントリーを読み込ませるとエラーが出るようになった。暫定的にエラーだけ出ないようにしたが、拡張子txtのエントリーがちゃんと読み込めていない(404になる)ので、要調査。

FreeBSD上のPythonのデフォルトバージョンが3.7から3.8に上がったので追従して上げてみたけど、結構面倒臭い結果に。やはりちゃんとテストしてから上げるべきだった…w

Python3系に移行した

posted by jun-g at Sun, 21 Jun 2020 11:51 JST

このブログは未だにPyBlosxomで運用しているわけですが、Python2系の公式サポートもそろそろ終了とのことなので、ここのPyBlosxomもPython3系に移行しました。

詳細は別途書きますが、まだpluginに細かいバグが残ってるのと、実行環境に調整の余地ありなので、チマチマ改善していきます。

取り急ぎ。

PyBlosxomのプラグイン

posted by jun-g at Mon, 23 Sep 2013 22:28 JST

バージョン1.5から標準でプラグインが同梱されるようになったので、同梱されてるものはなるべくそれを使うように設定を変更しました。

ついでに自作のプラグインはコードを整理してちょっとづつbitbucketに登録していってます。

daemonfreaks / pyblosxom-plugins

自分のブログをちょこちょこカスタマイズするのは箱庭的な趣があって、何年経っても楽しいものですね。

PyBlosxom-1.5-rc2に入れ替え

posted by jun-g at Sun, 17 Oct 2010 17:01 JST

8月頃にrc2がリリースされていた事に今頃気付いたので、入れ替えてみた。何が変わったのかは追っかけてない。

ついでにflavourを少しいじって最近ようやく流行ってきたfacebookの「いいね!」ボタンを付けてみた。最近どのサイト見てもこのボタンついてるなー。

参考: このサイトにフェイスブックの「いいね!」ボタンを追加しました - aquapple

PyBlosxom-1.5-rc1に入れ替え

posted by jun-g at Wed, 09 Jun 2010 22:00 JST

ずーっと前から連日コメントスパムの攻撃に合ったりentrycacheが動かなくなったりしながらもごまかして使ってたけど、今年の初めに1.5-rc1がリリースされてから手元でダラダラ動作確認してたのがようやく完了したので入れ替えた。

デザイン変えたり新しいプラグイン入れたり自作プラグインを書き直したりまだ色々したいけど、それはまた今度。

…今気づいたけど、タグで絞って表示してる時にrecent entriesが表示できてないな…。

wbgpagerを使うようにした

posted by jun-g at Sun, 06 Jul 2008 03:24 JST

前からこのブログにページャが付いていない事が気になっていたので、今更ながらページャのプラグインを使うようにした。追加したプラグインはwbgpager

最初、エラーも出ないのにページャが全然表示されず、ちょっとハマった。使用しているPyBlosxomのバージョンが古いせいかな、と思い、一つ前のバージョン(2007-11-30)のwbgpagerを使ってみると、ようやく動くようになった。

最近ブログを放置しすぎている気がするので、久々に色々手入れしようかな。

Google App EngineでPyBlosxomを動かす

posted by jun-g at Sun, 11 May 2008 06:20 JST

遅まきながら、Google App EngineのSDKで遊んでいます。入門記事を読みながらHello worldをこなし、次にPyBlosxomを動かしてみる事にしました。

まずはアプリケーションを配置するディレクトリを作成。

$ mkdir -p ~/gae/pb

次にPyBlosxomをダウンロードして展開し、先ほど作ったディレクトリの下に配置。

$ cd /tmp
$ fetch http://jaist.dl.sourceforge.net/sourceforge/pyblosxom/pyblosxom-1.4.3.tar.gz
$ tar zxvf pyblosxom-1.4.3.tar.gz
$ cp -R pyblosxom-1.4.3/Pyblosxom ~/gae/pb/.

データ用のディレクトリを作る。

$ mkdir -p ~/gae/pb/data/entries

config.pyを環境に合わせて書き換える。

$ cp /tmp/pyblosxom-1.4.3/web/config.py ~/gae/pb/.
$ vim config.py

datadirは"data/entries"と相対パスで指定。他はお好みで適当に。

次に~/gae/pbの直下にメインとなるモジュールpb.pyを作成。

import wsgiref.handlers

import Pyblosxom.pyblosxom
from google.appengine.ext import webapp

def main():
    application = Pyblosxom.pyblosxom.PyBlosxomWSGIApp()
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

同じく~/gae/pbの直下にapp.yamlを作成。

application: hello

version: 1

runtime: python

api_version: 1

handlers:

- url: /.*
  script: pb.py

これで準備完了。~/gae/pb/data/entriesの下に適当に書いたエントリのファイルを置いて起動。

$ dev_appserver.py ~/gae/pb  
INFO     2008-05-11 05:55:30,453 appcfg.py] Checking for updates to the SDK.
WARNING  2008-05-11 05:55:30,949 datastore_file_stub.py] Could not read datastore data from /tmp/dev_appserver.datastore
WARNING  2008-05-11 05:55:30,949 datastore_file_stub.py] Could not read datastore data from /tmp/dev_appserver.datastore.history
INFO     2008-05-11 05:55:30,954 dev_appserver_main.py] Running application hello on port 8080: http://localhost:8080

よし、ブラウザからアクセス!するとエラーが出る…。

...snip...
 /home/jun-g/gae/pb/Pyblosxom/tools.py in ()
 1282     __overlapped = pywintypes.OVERLAPPED()
 1283 elif os.name == 'posix':
 1284     import fcntl
 1285     LOCK_EX = fcntl.LOCK_EX
 1286     LOCK_SH = fcntl.LOCK_SH
fcntl undefined

: No module named fcntl
      args = ('No module named fcntl',)
      message = 'No module named fcntl'

fcntlというモジュールが使えないみたいなので、tools.pyを適当に書き換えてfcntlを使わないようにしてみる。

$ diff -u tools.py.orig tools.py
--- tools.py.orig	2008-05-11 05:59:14.000000000 +0900
+++ tools.py	2008-05-11 06:00:06.000000000 +0900
@@ -1281,10 +1281,9 @@
     # is there any reason not to reuse the following structure?
     __overlapped = pywintypes.OVERLAPPED()
 elif os.name == 'posix':
-    import fcntl
-    LOCK_EX = fcntl.LOCK_EX
-    LOCK_SH = fcntl.LOCK_SH
-    LOCK_NB = fcntl.LOCK_NB
+    LOCK_EX = None
+    LOCK_SH = None
+    LOCK_NB = None
 else:
     raise RuntimeError("PortaLocker only defined for nt and posix platforms")
 
@@ -1299,10 +1298,10 @@
 
 elif os.name =='posix':
     def lock(f, flags):
-        fcntl.flock(f.fileno(), flags)
+        pass
 
     def unlock(f):
-        fcntl.flock(f.fileno(), fcntl.LOCK_UN)
+        pass
 
 # END portalocking block from Python Cookbook.
 # %<-------------------------

でもってブラウザからアクセスすると、

PyBlosxom on Google App Engine SDK

動いた!という事で、今日はここまで。

コメントスパム

posted by jun-g at Mon, 11 Feb 2008 01:19 JST

ここのPyBlosxomにはコメントスパム対策として、contribパッケージに含まれているcheck_javascriptプラグインを使用しているんやけど、ここ数日、その仕組みを回避してスパムコメントが投稿されるようになった。面倒だったので放置してたけど、あまり溜めると後で消す作業の方がもっと面倒になりそうだったので、10数件溜まったところでスパムコメントを削除した。

このまま放置するとまたスパムコメントを投稿されそうなので、check_javascriptプラグインに、「http://」という文字列が3つ以上含まれるならリジェクトする、といういいかげんな処理を追加した。しばらくはこれでごまかせるはず。

PyBlosxomではてなスターを使うプラグイン

posted by jun-g at Tue, 11 Dec 2007 06:52 JST

超今更って感じやけど、PyBlosxomではてなスターを使う為のプラグインを作った。

ダウンロードはこちらからどうぞ。→ hatenastar.py

使い方。

まずはてなスターに自分のブログを登録してトークンを入手する。必要なのはトークンの文字列だけで、JavaScriptタグは不要。

次にconfig.pyでプラグインをロードするように設定し、

py["hatenastar_token"] = "入手したトークン"

の設定を追加する。

次にFlavourの編集。head.htmlのHEADタグ内に「$hatenastar_header」を追加し、story.htmlの星を表示したい部分に「$hatenastar_star」を、コメントアイコンを表示したい部分に「$hatenastar_comment」を追加する。

以上。

次に既知の問題。表示するエントリの内容を自前でゴリゴリJSONに変換してJavaScriptタグ内に吐き出しているだけなので、エントリ名にシングルクォーテーションが入ってるとJavaScriptでエラーになるはず。でもって、ほとんどテストしてないので他にも不具合あるかも。需要ないから誰も使わないし誰も直してくれないと思ってるので、そのうち自分で直したりキレイにしたりする予定。

需要ないけど一応「その他の参考情報 - はてなスター日記」に載せて欲しいのでトラックバック送っとくか。

tDiaryのテーマが使えるPyBlosxom用のFlavour その2

posted by jun-g at Sun, 09 Dec 2007 19:10 JST

PyBlosxomでtDiaryのテーマが使えるFlavour「pbdiary」をアップデートした。

ダウンロードはこちらからどうぞ。→ pbdiary-0.2

0.1からの変更点は、

  • HTML4からXHTML1.0に変更
  • RSS auto-discovery用のタグを追加
  • commentsプラグインに対応

の3点。詳しい使い方は同梱のREADMEを参照。全然詳しく書いてないけど。

ってか、当たり前やけど、PyBlosxomのプラグインが吐き出すタグと、tDiaryのプラグインが吐き出すタグが違いすぎて、これ以上のプラグインへの対応は無理かも。各プラグインをtDiary互換のタグを吐き出すように改造して、blosxom starter kitみたいにひとまとめにして配布するようにした方が制約も少なくて楽そうやな…。ちょっと考えよう。

tDiaryのテーマが使えるPyBlosxom用のFlavour

posted by jun-g at Sun, 02 Dec 2007 23:33 JST

インストール直後のPyBlosxomの画面って見るに耐えないというか、あまりにもみすぼらしすぎる。でも、自分でシコシコとテンプレート(Flavour)作るのも面倒。でも、本家サイトのFlavour Registory見ても、ちょっとしか登録されてないし…。PyBlosxomイラネ。

…というどうしようもない状況を少しは改善したいと思って、tDiary用の膨大なテーマをPyBlosxomで利用する為のFlavourを作ってみた。というか何となく作って寝かせてあったものを整理して公開することにした。まったく需要ないと思うけど。

ダウンロードはこちらからどうぞ。→ pbdiary-0.1

Plugin類にはまだ全然対応できていない。contributed pluginsに含まれているcomments/trackback系のPluginには近日中に対応させる予定。あくまで予定。

PyBlosxom 1.4.1 にバージョンアップした

posted by jun-g at Sun, 12 Aug 2007 17:12 JST

1.4が出たしバージョンアップしよう、と宣言しつつしばらく放置してたら何時の間にか1.4.1が出てたので、重い腰を上げて作業してみた。

まずはローカルのテスト環境を1.4.1に入れ替えてみて動作確認、と思って動かしてみたら全然動かん…。デバッグした結果、原因はWSGI Applicationなクラス「PyBlosxomWSGIApp」の中でconfigの設定を空辞書(厳密には違うけど)で上書きしてしまい、設定値がすべて参照できなくなってしまっている為、と判明。こんな誰でも引っかかりそうなバグがいつまでも残ってるわけないよね?と思ってsvnリポジトリを見てみたら、案の定rev.1802で修正済みやった。

一応修正パッチを晒しとこう。

--- pyblosxom.py.orig	2007-08-12 13:36:57.000000000 +0900
+++ pyblosxom.py	2007-08-12 13:49:37.000000000 +0900
@@ -468,7 +468,7 @@
         import config
         self.config = dict(config.py)
 
-        self.config = _config
+        self.config.update(_config)
         if "codebase" in _config:
             sys.path.insert(0, _config["codebase"])

ちなみにWSGI環境(っていう表現って妥当…?)で動作させない限りこのバグは発生しない。でもwsgirefがインストールされている場合、CGIで動作させても内部で強制的にwsgirefのCGIHandlerを使ってPyBlosxomWSGIAppクラスを実行する仕組みになっているので、このバグが発生してしまう。wsgirefはPython 2.5から標準ライブラリに含まれるようになったので、Python 2.5の人は特に要注意って事で。

PyBlosxom用vim plugin - pyblosxom.vim

posted by jun-g at Mon, 23 Apr 2007 21:59 JST

~/.vimrc がごちゃごちゃしてきたので、以前書いたPyBlosxom用のvimスクリプトを整理してvim pluginにしてみた。

ダウンロード

こちらからどうぞ

インストール

アーカイブを展開すると、pyblosxom.pyとpyblosxom.vimが出てくるので、それらを~/.vim/pluginに入れてインストール完了。

他に必要なもの

  • config.py … PyBlosxomの設定ファイル。
  • py-mixi … mixi日記に投稿する際に必要。(無くてもOK)

~/.vimrcの設定

~/.vimrcに以下の設定が必要。

  • g:pbconfigfile … config.pyファイルのパス
  • g:pbftpserver … FTPサーバ名
  • g:pbftpuser … FTPユーザー
  • g:pbftppassword … FTPパスワード
  • g:pbworkdir … ローカルの作業ディレクトリ
  • g:pbremotedir … アップロード先のディレクトリ
  • g:pbpingservers … 更新PINGを送信するPingサーバ
  • g:pbmixiid … mixiにログインするメールアドレス
  • g:pbmixipassword … mixiのパスワード

~/.vimrcには以下のように書けば良い。

:let g:pbconfigfile = "/home/jun-g/pyblosxom/config.py"
:let g:pbftpserver = "daemonfreaks.com"
:let g:pbftpuser = "user"
:let g:pbftppassword = "password"
:let g:pbworkdir = "/tmp"
:let g:pbremotedir = "/pyblosxom/entries"
:let g:pbmixiid = "mixi@loginid.com"
:let g:pbmixipassword = "mixipassword"
:let g:pbpingservers = "['http://pingoo.jp/ping/', 'http://api.my.yahoo.co.jp/RPC2', 'http://blogsearch.google.co.jp/ping/RPC2']"

使い方

  • 新規エントリ作成 -> 新しいエントリ(バッファ)にテンプレートを表示

    :PBCreateEntry
  • エントリのアップロード -> 現在編集中のエントリ(バッファ)を保存してアップロード

    :PBUploadEntry
  • トラックバック送信 -> 現在編集中のエントリから指定したURLへトラックバックを送信

    :PBTrackback [trackback url]
  • 更新PING送信

    :PBSendUpdatePing
  • 日記の抜粋をmixi日記に投稿 -> 現在編集中のエントリの抜粋を投稿

    :PBPost2Mixi

備考・ToDoなど

  • トラックバックで送信する本文は、エントリの先頭行に「...」を加えた文字列。
  • mixi日記に投稿する日記の抜粋は、エントリの先頭行に「...」を加えた文字列とエントリのURL。
  • Windows版vimには未対応。
  • カテゴリ(サブディレクトリ)には対応していない。なぜなら自分が使っていないから。次のバージョンで対応予定。
  • 更新PING送信機能は、以前どなたかのBlogで見かけたコードをほぼそのまま使用している。
  • mixi日記投稿機能は、以前実装したものを外部の関数として書き直したもの。

ご意見、ご要望、バグレポート、パッチなどなど、お待ちしています。

PyBlosxomでGoogle Sitemapを生成

posted by jun-g at Sun, 08 Apr 2007 19:13 JST

プラグインを探したけど見つからなかったので自分で作ってみた。sitemaps.orgプロトコルに準拠。

ダウンロードはこちらからどうぞ。

インストールは、sitemap.pyをプラグインディレクトリに入れて、config.pyのプラグイン設定に"sitemap"を追加するだけ。$base_url/sitemap.xmlにアクセスして表示されればOK。

アクセスがある度に生成するので、エントリが大量にあるblogではちょっと重いかも…。

entrycache導入

posted by jun-g at Mon, 02 Apr 2007 13:39 JST

PyBlosxomはファイルの更新日時がそのままエントリの投稿日時になるので、過去のエントリに追記しちゃうとそのエントリの日付が変わってしまって困るなー、って思ってたら、投稿日時を記憶するプラグインがあったことに今頃気づいた…。

ということで、ファイル名やエントリの配置などの細工なしで導入できそうなentrycacheをプラグインに追加。初回起動時に過去エントリのキャッシュを一気に作成するので、少し重かった。

あと、mod_wsgi環境ではキャッシュファイルに投稿日時が保存されなくて少しハマッた。mtimeが取得できてない?でも投稿日時の表示はできてるしなぁ。今度デバッグしてみよう。

PyBlosxom on mod_wsgi

posted by jun-g at Thu, 22 Mar 2007 03:36 JST

mod_wsgiのリリース間近でなんとなく盛り上がっているふうなので、僕も試してみた。

やっぱり動かすならPyBlosxomだよねぇ(Wikiにも載ってないし)という事で、Apache2.2 + PyBlosxom(svn trunk版)で動かす事に。

インストールはaodag隊長んとこに書いてあるとおり、

./configure && make && make install

で完了。当然事前にpythonとapacheがインストールされている必要あり。後はディレクトリ作ってPyBloxsomのソース一式を突っ込んでから、Apacheの設定ファイルに以下を追加した。

LoadModule wsgi_module libexec/apache22/mod_wsgi.so
(略)
WSGIScriptAlias /pyblosxom /usr/local/www/apache22/wsgi/wsgi_app.py
<Directory "/usr/local/www/apache22/wsgi">
(略)

で、あっさり動いた。素晴らしい。っていうか元々WSGI対応の作りになっているので当然そのままで動くんやけど。

Pyblosxom on Mod_Wsgi
Pyblosxom on Mod_Wsgi posted by (C)jun-g

mod_wsgiで動かすのは簡単やったけど、trunk版のPyBlosxomに新しい設定項目(locale)が追加になってて、そこに少しハマったという…。

comments, trackback, check_javascript, tb_spamプラグイン導入

posted by jun-g at Mon, 18 Dec 2006 03:24 JST

ようやく導入してみた。わくわく。trackbackプラグインの修正に合わせて、tb_spamプラグインも以下の通り修正してから導入した。

--- tb_spam.py.orig	Mon Dec 18 02:59:37 2006
+++ tb_spam.py	Mon Dec 18 03:09:12 2006
@@ -11,25 +11,20 @@
 __VERSION__ = '0.1'
 __LICENSE__ = 'BSD license'
 
-def cb_comment_reject(args):
+def cb_trackback_reject(args):
     request = args['request']
     comment = args['comment']
-    if comment.has_key('trackback'):
-        import urllib
-
-        config = request.getConfiguration()
-        my_url = config['base_url']
-        if (comment.has_key('link')):
-            f = urllib.urlopen(comment['link'])
-            content = f.read()
-            f.close()
-
-            if content.find(my_url) == -1:
-                return True
-            else:
-                return False
-        else:
+    import urllib
+    config = request.getConfiguration()
+    my_url = config['base_url']
+    if comment.has_key('link'):
+        f = urllib.urlopen(comment['link'])
+        content = f.read()
+        f.close()
+        if content.find(my_url) == -1:
             return True
-
+        else:
+            return False
     else:
         return False

上手く動きますように!!

コメントスパム対策系プラグインでトラックバックまで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プラグインの修正は…もうちょっと考えてみよう。

pyblosxomのSVN trunkにcheck_javascriptプラグインが追加されてた!

posted by jun-g at Fri, 15 Dec 2006 03:30 JST

昨日、pyblosxomのSVN trunkにcheck_javascriptプラグインが新たに登録されてた。どうもJavaScriptを利用したコメントスパム対策用のプラグインみたい。という事は、こいつを利用すればMagicWordプラグインは不要、さらにtrackbackプラグインへの修正も不要って事かー!ムホムホ!!という事で早速試してみた。

まずはいつも通りcheck_javascript.pyをpluginsディレクトリにコピーして、config.pyを編集してプラグインがロードされるように設定。そして、コメントフォーム用のflavourであるcomment-form.htmlを、trunkの雛型を参考にして以下の通り修正。

--- comment-form.html.orig	Sun Oct 15 02:59:40 2006
+++ comment-form.html	Fri Dec 15 03:00:11 2006
@@ -3,7 +3,7 @@
 <div class="blosxomCommentForm">
 <form method="post" action="$base_url/$file_path.html#commentMessage" id="comments_form">
 <fieldset>
-<input type="hidden" name="secretToken" value="pleaseDontSpam" />
+<input type="hidden" name="secretToken" id="secretTokenInput" value="pleaseDontSpam" />
 <input name="parent" type="hidden" value="$file_path" />
 <input name="title" type="hidden" value="$title" />
 <label class="commentForm">Name</label><br />
@@ -21,6 +21,11 @@
 <input name="submit" type="submit" value="Post" />
 </fieldset>
 </form>
+<script type="text/javascript"><!--
+// used by check_javascript.py. this is almost entirely backwards compatible,
+// back to 4.x browsers.
+document.getElementById("secretTokenInput").value = "$blog_title";
+//--></script>
 </div> <!-- ends blosxomCommentForm div -->
 </div>
 </div> <!-- ends blosxomComments div -->

パラメータ「secretTokenInput」の値にJavaScriptで$blog_titleの値を設定し、プラグイン側のcb_comment_reject()で値をチェックする、という仕組みらしい。お手軽やね。あ、でもJavaScript使うって事は、テキストブラウザや古いブラウザからはコメント投稿できないって事かー!ま、いいか。

という事で、簡単さに惹かれてこいつを導入する事に決定。効果の程はこれいかに。

trunkのcomments plugin

posted by jun-g at Tue, 12 Dec 2006 03:00 JST

先日のtrackback pluginのパッチは無事に取り込まれた模様。よかった。

そのパッチを作る際、trunkのcomments.pyを軽く見たんやけど、なんかメール送信時の非ASCII文字を受け付けない問題とか色々修正されてるっぽかったので、近々試してみないとなぁと思ってたら、shunuhsさんがtrunkに入れ替えた上にパッチを書いて、しかもtb spam用のプラグインまで作ってた。素晴らしいっす。仕事早いっす。動作実績があると心強いので、早速僕もtrunkのcomments/trackback pluginに入れ替える事にした。最近コメントスパムに悩まされていたので、ついでにMagicWord pluginも導入することに。

が、その前にshunuhsさんが「メールが飛ばない」と言われている問題を調べてみる事にした。テスト環境のcomments.pyをtrunkのものに入れ替えて試してみると、pyblosxomのエラーログに

2006-12-12 00:07:34,909 [ERROR] comments: error sending email: ['Traceback (most recent call last):\n', '  File "/pub/www/pyblosxom/plugins/comments.py", line 524, in send_email\n    msg=body)\n', '  File "/usr/local/lib/python2.4/smtplib.py", line 692, in sendmail\n    (code,resp) = self.data(msg)\n', '  File "/usr/local/lib/python2.4/smtplib.py", line 485, in data\n    q = quotedata(msg)\n', '  File "/usr/local/lib/python2.4/smtplib.py", line 193, in quotedata\n    re.sub(r\'(?:\\r\\n|\\n|\\r(?!\\n))\', CRLF, data))\n', '  File "/usr/local/lib/python2.4/sre.py", line 142, in sub\n    return _compile(pattern, 0).sub(repl, string, count)\n', 'TypeError: expected string or buffer\n']

というのが出てた。どうもメール送信時にエラーになってるっぽい。で、なんとなくピンときて入れた修正が以下。

--- comments.py.orig	Tue Dec 12 01:52:47 2006
+++ comments.py	Tue Dec 12 02:36:47 2006
@@ -521,7 +521,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:

予想適中、無事にメール送信できるようになった。しかーし!現状のcomments.pyの作りだと、メールヘッダに設定されるべき「From」「To」「Subject」がメール本文に記載されるという超手抜き仕様。現状だとこんな本文のメールが来る。

From: from_at_aemonfreaks.com
To: to_at_daemonfreaks.com
Date: Mon, 11 Dec 2006 16:20:32 -0000
Subject: comment by TEST

Name: TEST
URL: 
Hostname: localhost.daemonfreaks.local (192.168.0.21)
Entry URL: http://localhost/blog/200611060000
Comment location: /pub/www/pyblosxom/entries/comments/200611060000-1165854032.63.cmt


Hello, world.

しかも件名が空。イケてない。先頭の4行は明らかにメールヘッダにあるべき。なので、以前自分で修正したやつと同様の修正を今回も入れる事にした。修正内容は以下のとおり。

--- comments.py.orig	Tue Dec 12 01:52:47 2006
+++ comments.py	Tue Dec 12 01:48:48 2006
@@ -475,13 +475,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 +494,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 +508,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 +523,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:

これで「Subject」「From」「To」(「Date」はオマケ)が正しくメールヘッダに設定されるようになった。ちなみに「comment_mta_cmd」を使用してメール送信を行う場合の動作は未確認なので、どなたか試してみてください…。

と、ここまで調べたところで時間切れになってしまった。続きは明日以降で。Magic Wordとtb_spam.pyの動作確認をしてから全部導入することにしよう。

trackbackプラグインのパッチ

posted by jun-g at Sun, 26 Nov 2006 21:29 JST

shunuhsさんに勧められたので、以前作ったtrackbackプラグインのパッチを本家に取り込んでもらうべくpyblosxom-develのMLに投げてみたよ。

英語書くっていうレベルじゃねぇぞ的な拙い英文は相変わらず。割と適当な英文でも結構伝わるものなので大丈夫なはず。根拠は無いけど。

トラックバックスパムが

posted by jun-g at Wed, 15 Nov 2006 20:49 JST

昨日、一昨日と大量のトラックバックスパムを喰らってしまった。昨日は仕事が終わって家に帰ってきたのがAM2:30頃で、それから大量のスパムに気付いて、慌てて削除したんだけど、このまま放置してたら今日も喰らっちゃうだろうなぁ、と思ったので、眠かったけどとりあえず急いでtrackback.pyを修正してスパム避けを実装しておいた。

--- trackback.py.orig	Wed Nov 15 02:58:41 2006
+++ trackback.py	Wed Nov 15 03:12:36 2006
@@ -87,6 +87,16 @@
         message = "A trackback must have at least a URL field (see http://www.sixapart.com/pronet/docs/trackback_spec )"
 
         if form.has_key("url"):
+            try:
+                import urllib
+                ret = urllib.urlopen(form['url'].value)
+                target = ret.read()
+                ret.close()
+            except:
+                target = ""
+            if not config['base_url'] in target:
+                print >> response, tb_bad_response % "Please paste any links to your blog."
+                return 1
             from comments import decode_form
             decode_form(form, config['blog_encoding'])
             import time

いわゆる言及リンクのないトラックバックは受け付けないようにしたかったんだけど、trackback.py内で当該エントリのURLを取得する方法を調べる余裕がなかったので、結局ここのサイトのURLがあればオッケーという適当な作りに。送信元ページ内のチェックもかなり適当。週末時間が取れたらもうちょっとちゃんと書き直す。つもり。

trackbackプラグイン導入 その2

posted by jun-g at Sun, 15 Oct 2006 03:53 JST

はてなダイアリーからTrackBackを送信してみたところ、レスポンスは正常に返ってきたのに、コメント欄表示でエラーが出るようになった。TrackBackを受信して出来たコメントファイルも空だったので調べてみると、ここのコメント欄に書いてあるとおり、trackback.py内で文字が適切にエンコードされていない、という事らしい。

というわけで、trackback.pyを以下の通り修正した。

--- trackback.py.orig   Sun Oct 15 03:24:03 2006
+++ trackback.py        Sun Oct 15 03:26:32 2006
@@ -87,6 +87,8 @@
         message = "A trackback must have at least a URL field (see http://www.sixapart.com/pronet/docs/trackback_spec )"

         if form.has_key("url"):
+            from comments import decode_form
+            decode_form(form, config['blog_encoding'])
             import time
             cdict = { 'title': form.getvalue('title', ''), \
                       'author': 'Trackback from %s' % form.getvalue('blog_name', ''), \

もう一度はてなダイアリーからTrackBackを送信すると、無事に受信・表示できるようになった

それにしても、今回の件だけでなく、pyblosxomを設置するにあたって、SH1.2 PYBLOSXOMpyblosxom関連のエントリをとても参考にさせてもらった。これらのエントリがなかったらpyblosxomの導入を途中で挫折してたかも。ありがとうございます。

trackbackプラグイン導入

posted by jun-g at Sun, 15 Oct 2006 03:05 JST

commentsプラグインに続いてtrackbackプラグインを導入してみた。

commentsプラグインでメール送信 導入

posted by jun-g at Sat, 14 Oct 2006 06:48 JST

メール送信部分の修正、スタイルシートの調整が完了したので、commentsプラグインを導入してみた。

comments.pyの修正箇所はこんな感じ。

--- comments.py.orig	Thu Oct 12 23:07:08 2006
+++ comments.py	Sat Oct 14 06:39:54 2006
@@ -450,26 +450,21 @@
         from rfc822 import formatdate
     from socket import gethostbyaddr
 
+    from email.Header import Header
+    from email.MIMENonMultipart import MIMENonMultipart
+
     author = escape_SMTP_commands(clean_author(comment['author']))
     description = escape_SMTP_commands(comment['description'])
     ipaddress = escape_SMTP_commands(comment.get('ipaddress', '?'))
 
-    if comment.has_key('email'):
-        email = escape_SMTP_commands(clean_author(comment['email']))
-    else:
-        email = config['comment_smtp_from']
-
     try:
         server = smtplib.SMTP(config['comment_smtp_server'])
         curl = config['base_url']+'/'+entry['file_path']
-        comment_dir = os.path.join(config['comment_dir'], entry['absolute_path'])
-
+        from_addr = config['comment_smtp_from']
+        to_addr = config['comment_smtp_to']
+        subject = "comment by %s" % author
+        enc = config["blog_encoding"]
         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'])
@@ -479,12 +474,19 @@
             message.append("Hostname: %s (%s)" % (gethostbyaddr(ipaddress)[0], ipaddress))
         except:
             message.append("IP: %s" % ipaddress)
-        message.append("Entry URL: %s" % curl)
+        message.append("Entry URL: %s.html" % curl)
         message.append("Comment location: %s" % comment_filename)
         message.append("\n\n%s" % description)
-        server.sendmail(from_addr=email,
-                        to_addrs=config['comment_smtp_to'], 
-                        msg="\n".join(message))
+        msg = "\n".join(message)
+        part = MIMENonMultipart("text", "plain")
+        subj = Header(subject.encode(enc), enc)
+        part["Subject"] = subj
+        part["From"] = from_addr
+        part["To"] = to_addr
+        part.set_payload(msg.encode(enc), enc) 
+        server.sendmail(from_addr=from_addr,
+                        to_addrs=to_addr,
+                        msg=part.as_string())
         server.quit()
     except Exception, e:
         logger = tools.getLogger()

ついでにエントリの固定リンクに拡張子を付けるようにしたり、Recent Entriesを最大10件までの表示に変更したりした。

commentsプラグインでメール送信

posted by jun-g at Fri, 13 Oct 2006 01:05 JST

ローカルのテスト環境ではとりあえずメール送信できるようになった。まだ気に入らない部分があるので、もうちょっと修正してから週末に導入する。

commentプラグイン

posted by jun-g at Thu, 12 Oct 2006 01:19 JST

commentsプラグインを導入しようとしたけど、メール送信部分が日本語を受け付けない仕組みになっていたので導入延期。修正して送信できるようになってから導入しようっと。

vimでpyblosxomのエントリを書いてアップロードするvimスクリプトをpythonで書いた

posted by jun-g at Mon, 09 Oct 2006 22:53 JST

pyblosxomは、テキストファイルにエントリを書いてFTPでサーバにアップロードする、という仕組みになっている。プラグインで拡張する事でMetaWeblogAPIを使ったり投稿画面を付けたりする事ができるみたいやけどまだ全然試せていなくて、エディタでエントリを書いてアップする、という作業がかなり面倒に感じていた。

そんな時、「pythonを使ってvimからGoogleカレンダーに予定を投げる (ueBLOG)」というエントリを読んで、ちょうどエディタはvimを使ってるし、「Pyblosxom用エントリファイルのFTP送信」を読んで「Emacs便利でいいなー」とか思ってたので、vimでエントリを書いてそのままアップロードできるスクリプトをpythonで書いてみた。

python << EOF

FTP_SERV = "your.ftp.server"
FTP_USER = "ftpuser"
FTP_PASS = "ftppasswd"
LOCAL_DIR = "/path/to/localdir"
REMOTE_DIR = "/path/to/remotedir"

import vim
import datetime
from os import path
from ftplib  import FTP

def createEntry():
    fileName = path.join(LOCAL_DIR, _createFileName())
    vim.command("new %s" % fileName)
    vim.command("set fileencoding=utf-8")
    cb = vim.current.buffer
    cb[0] = "title"
    cb.append("#tags hoge,fuga")
    cb.append("body")
    vim.command("w")

def uploadEntry():
    cb = vim.current.buffer
    if cb.name == None:
        fileName = _createFileName()
        fullName = path.join(LOCAL_DIR, fileName)
        vim.command("saveas %s" % fullName)
    else:
        fileName = path.basename(cb.name)
        fullName = cb.name
        vim.command("w")
    f = open("%s" % fullName, "r")
    ftp = FTP(FTP_SERV, FTP_USER, FTP_PASS)
    ftp.cwd(REMOTE_DIR)
    ftp.storlines("STOR %s" % fileName, f)
    ftp.close()
    f.close()

def _createFileName():
    return "%s.txt" % datetime.datetime.now().strftime("%Y%m%d%H%M")

EOF

これを.vimrcに書いといて、vim上で

:py createEntry()

とすると

title
#tags hoge,futa
body

という空のエントリを新規バッファを開きつつ「LOCAL_DIR」で指定したディレクトリ内に保存する。ファイル名は現在日時を「YYYYMMDDHHMI.txt」として自動生成。二行目の「#tags〜」はtagsプラグインを使っているので。

でもってエントリをつらつら書いたら、

:py uploadEntry()

でアップロード。僕はエントリのカテゴリ分けをしていないので、カテゴリ指定は無し。

vimは全然使いこなせてないので、おかしな事してるところがあるかもしれないけど、これだけでかなり便利になった。vimももっと勉強しないとな。