公的SSL証明書は,お高い

会社から,よく PC の設定など自分のメモを見るために,自分の blog へアクセスすることがある.
メモを会社から投稿することもある.
でも,ちょっと情報セキュリティに気を使う会社なら投稿内容の監視くらいはしていそうなので,
SSL 通信で投稿内容を暗号化したいなーとか思ったのでした.

SSL の証明書って,たっけーのね…
最大手 VeriSign の場合,最安で 85,050 円とか,泣ける.
で,安いとこないかなーと探したら,こんなトコがありました → サイフにやさしいSSL証明書
2,880 円~ とか.びっみょー.
オレオレ証明書もいいけど,どうせなら第三者機関の証明書が欲しい…という人にとっては, 240円/月はお手頃なのだろうか.

spammer counter v3

IP アドレスでアクセスを禁止している spammer が,その後どれだけアクセスを続けているかをカウントする php コードを,今回は MySQL を使って実装した.
まず最初に注意点. ErrorDocument 403 で php ファイルへ飛ばす場合,その php ファイルへのアクセスは可能とすること.
さもなくば, 403 の転送で無限ループが発生する.
具体的には,.htaccess へ下記を追記.hogehoge.phpの部分は,設置したphpファイル名に変更すること.

<Files hogehoge.php>
allow from all
</Files>

spammer のアクセスが集中している時など,どうしても
spammer counter v2
では,ファイル書き込みに失敗するようだ.
その結果,度々ファイルがクリアされてしまっていた.

んで,よく考えたらこのサイトでは MySQL を使う blog が2つも動いていることを思い出したので,
DB で実装することにした.
とても楽チン.

そのコードは,こんな感じ.

<?php
header('HTTP/1.1 403 Forbidden');
header('Content-Type: text/html; charset=iso-8859-1');
?>
<!DOCTYPE HTML PUBLIC '-//IETF//DTD HTML 2.0//EN'>
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
<?php printf('You don't have permission to access %s\non this server.',
             htmlentities(strip_tags($_SERVER['REQUEST_URI']))); ?><P>
<HR>
<ADDRESS><?php
$e = explode(' ',$_SERVER['SERVER_SOFTWARE']);
printf('%s Server at %s Port %d',$e[0],$_ENV['SERVER_NAME'],$_ENV['SERVER_PORT']);
?></ADDRESS>
</BODY></HTML>
<?php
// MySQL
$mySqlHost = 'xxx.xxx.xxx';
$dbname    = 'database_name';
$user      = 'user_name';
$password  = 'password';
$table     = 'spammer_counter';

// connecting
$mobj = mysql_connect($mySqlHost,$user,$password);
if ($mobj == FALSE) { die('broken');}

// count IP addr
$addr = $_SERVER['REMOTE_ADDR'];

mysql_select_db($dbname,$mobj);
$done=mysql_query('insert into '.$table.' (ipaddr,number) values (''.$addr.'',1) '
                  .'on duplicate key update number=number+1;'); 
mysql_close($mobj);
if ($done == FALSE) { die('no response...');}
?>

欠点は,データを見るのに DB を叩く必要があること.  
それは嫌なので,viewer も作った.
</p>
<p class="pre">
<?xml version='1.0' encoding='Shift_JIS'?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
    'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='ja' lang='ja'>
 <head><title>spammer viewer</title></head>
 </body>
 <h1>spammer viewer</h1>
 <table style='border: black solid 1px;'>
  <tr><th>IP addr</th><th># of access</th></tr>
<?php
// MySQL
$mySqlHost = 'xxx.xxx.xxx';
$dbname    = 'database_name';
$user      = 'user_name';
$password  = 'password';
$table     = 'spammer_counter';

// connecting
$mobj = mysql_connect($mySqlHost,$user,$password);
if ($mobj == FALSE) { die('broken');}

mysql_select_db($dbname,$mobj);

$done=mysql_query('select * from '.$table.';');
mysql_close($mobj);
if ($done == FALSE) { die('no response...');}

while ( $row = mysql_fetch_row($done)) {
  printf('  <tr><td>%s</td><td style=\'text-align:right;\'>%d</td></tr>\n',$row[0],$row[1]);
}

?>
 </table>
 </body>
</html>

spammer counter v2

spammer counter の記事で書いた,403 にかかった IP を保存するスクリプトを改造した.
まず最初に注意だが, ErrorDocument 403 で php ファイルへ飛ばす場合,その php ファイルへのアクセスは可能とすること.
さもなくば, 403 の転送で無限ループが発生する.
具体的には,.htaccess へ下記を追記.hogehoge.phpの部分は,設置したphpファイル名に変更すること.

<Files hogehoge.php>
allow from all
</Files>

もともとこのスクリプトを設置した理由は,弾いている IP を subnet mask でまとめるため,
subnet 毎のアクセスを把握したかったためなのだが,
先の記事のログではアクセス順に並ぶため, subnet 毎の状況を一見して把握できなかった.
そこで,アクセスがある毎に, 1 回だけ bubble sort を走らせることにした.

bubble sort で実装し行毎処理を行なっているのは,
全てメモリに読み込んで sort するのは,spammer のアドレスが想像以上に多いので,
ライン数が増えて処理に時間がかかるようになり,ファイルロックに邪魔されて
spammer のアクセスを拾えなくなるのではないかという懸念のため.
実際には,File I/O の方がボトルネックになっている気がするので,
fwrite 一発で書き込んだ方が処理は早いかも知れない…
もっと行数が増えたら,測定してみよう.

で,まぁコードはこんな感じ.

-- 追記
ごめん,バグがあった.
ループ前の1回の処理でbreak文を消してなかったので,バグってた.

-- 追記2
fgetcsv で取得した末尾の空白文字を取り除かないと \n が入ってしまうらしいことに,
後から気付いた.
全部 int キャストすることで,回避.

<?php
header('HTTP/1.1 403 Forbidden');
header('Content-Type: text/html; charset=iso-8859-1');
?>
<!DOCTYPE HTML PUBLIC '-//IETF//DTD HTML 2.0//EN'>
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
<?php printf('You don't have permission to access %s\non this server.',
             htmlentities(strip_tags($_SERVER['REQUEST_URI']))); ?><P>
<HR>
<ADDRESS><?php
$e = explode(' ',$_SERVER['SERVER_SOFTWARE']);
printf('%s Server at %s Port %d',$e[0],$_ENV['SERVER_NAME'],$_ENV['SERVER_PORT']);
?></ADDRESS>
</BODY></HTML>
<?php
// comparison 2 IP addresses
function aIsLargerThanB($a,$b) {
    // a and b are arrays of 4 integers, e.g. 127.0.0.1 is expressed as
    // array(127,0,0,1)
    if ($a[0]==$b[0])
        if ($a[1]==$b[1])
            if ($a[2]==$b[2]) return $a[3] > $b[3];
            else return $a[2] > $b[2];
        else return $a[1] > $b[1];
    else return $a[0] > $b[0];
}

// parse IP address to array of 4 integers.
function parseIpAddress($ip_addr){
    $return = array();
    foreach ( explode('.',$ip_addr) as $i) $return[] = (int)$i;
    return $return;
}

// count IP addr
$addr = $_SERVER['REMOTE_ADDR'];
$fobj = fopen('counter.dat','r');
if (flock($fobj, LOCK_EX)) {
  $oobj = fopen('counter.tmp','w');
  $flag=false;

  // 1st processing
  $s=fgetcsv($fobj);
  if (count($s)==2 and $s[0] === $addr) {
    $s[1]=(int)$s[1]+1;
    $flag=true;
  } else { $s[1] = (int)$s[1];}
  $prev = array($s[0],$s[1]);

  // process loop
  while (!feof($fobj))
    {
      $s=fgetcsv($fobj);
      $s[1] = rtrim($s[1]);
      if (count($s)<2) {
    fputcsv($oobj,$prev);
    break;
      } elseif (!$flag and $s[0] === $addr) {
    $s[1]=(int)$s[1]+1;
    $flag=true;
      } else { $s[1] = (int)$s[1];}

      $pips = parseIpAddress($prev[0]);
      $nips = parseIpAddress($s[0]);

      // bubble sort
      if (aIsLargerThanB($pips,$nips)) {
    fputcsv($oobj,$s);
      } else {
    fputcsv($oobj,$prev);
    $prev = array($s[0],$s[1]);
      }

    }

  fputcsv($oobj,$prev);
  if (!$flag) { fprintf($oobj,'%s,1\n',$addr);}
  flock($fobj,LOCK_UN);
  fclose($fobj);
  fclose($oobj);
  copy('counter.tmp','counter.dat');
} else {
    fclose($fobj);
}
?>

spammer counter

アクセス禁止したスパマーが,その後どれだけアクセスしているかカウントする.

以前,スパム増加という記事でも触れたが,
本サイトではアクセス禁止リストにあるホストからのアクセスを禁止している.
ところが,似たようなIPからのアクセスが絶えず続いており,どんどんホストを追加している状態である.
このままではアクセス禁止リストが延々と伸びていってしまうので,特にアクセスの多いアドレス空間については,
適当なサブネットマスクでもってアク禁とすることにしたい.

ところが,一度アクセスを禁止してしまうと,その後はどの程度のアクセスがあるのか分からない.
アク禁リストのアップデートをするための情報として,アク禁を解除しないまま,アクセス数を把握する方法を考えた.

ずばり,403 エラードキュメントをphpにして,アクセス元をカウントアップする作戦である.
そして普通に誤った操作をした人に疑問を与えないよう,403を忠実に再現したい.
ということで,こんなコードを生成してみた.

<?php
header("HTTP/1.1 403 Forbidden");
header("Content-Type: text/html; charset=iso-8859-1");
?>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>403 Forbidden</TITLE>
</HEAD><BODY>
<H1>Forbidden</H1>
<?php printf("You don't have permission to access %s\non this server.",
             htmlentities(strip_tags($_SERVER['REQUEST_URI']))); ?><P>
<HR>
<ADDRESS><?php
$e = explode(' ',$_SERVER['SERVER_SOFTWARE']);
printf("%s Server at %s Port %d",$e[0],$_ENV['SERVER_NAME'],$_ENV['SERVER_PORT']);
?></ADDRESS>
</BODY></HTML>
<?php
// count IP addr
$addr = $_SERVER['REMOTE_ADDR'];
$fobj = fopen("counter.dat","r");
if (flock($fobj, LOCK_EX)) {
    $oobj = fopen("counter.tmp","w");
    $flag=false;
    for(;!feof($fobj);)
    {
        if ($flag) { fwrite($oobj,fgets($fobj));}
        else {
            $s=fgetcsv($fobj);
            if (count($s)<2) { break;}
            if ($s[0] === $addr) {
                $s[1]=(int)$s[1]+1;
                $flag=true;
            }
            fputcsv($oobj,$s);
        }
    }
    if (!$flag) { fprintf($oobj,"%s,1\n",$addr);}
    fclose($fobj);
    fclose($oobj);
    copy("counter.tmp","counter.dat");
} else {
    fclose($fobj);
}
?>

ファイルロックなんて,何年か振りに実装した.
普通に php のオンラインドキュメントにあるようなファイルロックにしていないのは,
各行毎に読み込み/書き込みをしたかったから.
読み込み用ファイルを排他ロックして,読み込めた人だけ処理が進むようにしてみた.
そんな処理が問題になるほど膨大なログが溜まる頃には,レンタルサーバのスペースを
食い潰してアカウント消されていそうだけどね…

んで,アク禁で ErrorDocument 403 へ飛ばす場合の注意.
403 で飛ばす先の php ファイルへのアクセスは可能とすること.
さもなくば, 403 の転送で無限ループが発生する.

<Files hogehoge.php>
allow from all
</Files>

とかでOK.

余談だが, ErrorDocument で http://~~ と URI 指定すると,
別サーバへの転送と同様に処理されてしまうらしく,呼び出し元が引けなくなるので注意.

AutoFollowersBlocker

GAE 上で動作し, cron で呼び出されて, twitter アカウントの follower を監視し,
プロファイルの文字列によって自動ブロッキングをするスクリプトを作成した.
きっかけは,昔むかし,山のように spammer からフォローされまくって,鬱陶しかったため.
当時の記事はこちら → twitterのspammerのfollowが多い

で,作ってみたのが, twitter を OAuth 認証でアクセスするためのクラス twit_oauth.py
そして,呼び出し元の AFBsample.py
いずれも分量が多いので,ソース自体はリンク先を参照されたい.

また,GAEで使うためのyamlファイルもついでに貼り付けしておく.

application: afb-sample
version: 10
runtime: python
api_version: 1

handlers:
# handling mode
- url: /cron
  script: AFBsample.py
  login: admin
# viewer mode
- url: /.*
  script: AFBsample.py
cron:
- description: polling job
  url: /cron
  schedule: every 5 minutes

さてこれで,次の4つを GAE へ放り込めば動く.
GAE の使い方については,web を参照されたいが,以前に hadacchi が調べたメモもある → twitterのspammerのfollowが多い
これでも,多少は参考になるかも知れない.

  • AFBsample.py
  • twit_oauth.py
  • app.yaml
  • cron.yaml

さて,今回のハマりポイントは,呼出 URL による処理の振り分けだった.

handlers:
# handling mode
- url: /cron
  script: AFBsample.py
  login: admin
# viewer mode
- url: /.*
  script: AFBsample.py
application = webapp.WSGIApplication([
    ('/cron', AutoFollowerBlocker),
    ('/', AFBview)
    ])

def main():
    wsgiref.handlers.CGIHandler().run(application)

当初は app.yaml の url 行を修正すれば呼び出し URL に応じて処理を振り分けできると思っていたが,
呼び出される側の python スクリプト内の webapp.WSGIApplication() の意味に気付いていなかった.
こいつの引数は,長さ 2 のタプルのリストであるが,タプルの第 1 要素は呼び出しURLになっていて,
ここでも呼び出し URL に応じて処理クラスを振り分けできる.
きちんと GAE の処理を読んでいないが,多分 app.yaml による振り分けは login 行におけるパーミッション制御や,
スクリプトファイルを分けることにより,クロスサイトスクリプティングみたいなアタックに対してある程度の
対処をしたい場合などに使うのが良いのかも知れない.

今回の構成では,
http://pjname.appspot.com/ へのアクセスで前回の実行結果を表示し,
http://pjname.appspot.com/cron へのアクセスでフィルタを手動実行する.

他の工夫としては,運用面で使い易くするため,自動 BLOCK 後に手動で UNBLOCK したユーザは対象から除外するようにしたり,
AND/OR 検索で重複マッチを走らせないようにしたり,
空フィールドに対して None が返却されるので事前に置換しておいたり,(デバッグの時にはjsonファイルを直接食わせる関係から,
マッチ関数内にもNoneの処理は残しているが…)
といった小細工は少ししてみた.

今後の課題としては,プロファイルを 1 call で 100 id 分まで抜ける API を見落としていたことが
分かったので,この API へ対応させた上で, follower が 10000 を越えるユーザでも TwitterAPI の
呼出制限にかからないよう初期化する実装を行なうことがある.
と難しく言ってみたものの,既チェック ID をパスするロジックなので, API の回数制限にかかる手前で
ループを break させてしまえば良いだけである.

参考にした web site は, Python アプリケーション設定 - Google App Engine - Google Code などの GAE のオンラインドキュメントと,
REST API Resources | Twitter Developers などの Twitter のオンラインドキュメントに加え,
前回までの記事 twitterでspammerのfollowを自動block 及び twitterのspammerのfollowが多い 内で参照している各ページである.

twitterでspammerのfollowを自動block

-- 追記2
ごめん,バグあった.文字列操作まわりのencodingがかなり怪しい.
空の時,Noneが入ってくるのは空文字列に置き換えるべきか.

-- 追記
きっと誰も AND/OR 検索を実装してくれないので,実装した.
ついでに, twit_oauth.py を更新して,フォロワだけでなくフォローしてる人の一覧も取れるようにした.

先の記事twitterのspammerのfollowが多いでも書いた,spammerのキーワード抽出について.
GMail からの PUSH 通知をトリガとした動作がうまく実装できなかったので (なぜか python2.5.xでは,imaplib2 がうまく動かない)
とりあえずトリガーは何も考えてません.cron でも,IMAP/IDLE でも,好きに実装してください.

twitter を変なライブラリなしで OAuth 認証で動かすクラスは,こんな感じで実装してます → twit_oauth.py
で,こいつを使って,下記のように使うとスパムフィルタのできあがり.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import simplejson,re
from twit_oauth import twit_oauth

__KEYWORDS = [(re.compile(u'キーワード1'),),
              (re.compile(u'キーワード2-1'),re.compile(u'キーワード2-2'))
             ]

# プロファイルリスト取得関数
def getProfiles(followers,pobj):
    profiles = {}
    for f_id in followers:
        profs=simplejson.loads(pobj.callAPI('profile',`f_id`))
        if profs['name']==None: name=''
        else: name=''.join(profs['name'].split('\n'))
        if profs['description']==None: desc=''
        else: desc=''.join(profs['description'].split('\n'))
        profiles[`f_id`]=(name,desc)
    return profiles

# テキストマッチ関数
def filterByKeys(profiles):
    fil_list = []
    for (id,prof) in profiles.items():
        # __KEYWORDS のいずれかの要素1つでも合致すればブロック (OR)
        for keys in __KEYWORDS:
            fil_list.append(id)
            # __KEYWORDS の1要素の中の,全てのマッチが成功すればブロック (AND)
            for k in keys:
                if not k.search(prof):
                    del fil_list[-1]
                    break
            # 重複登録回避
            if len(fil_list) != 0 and fil_list[-1] == id: break
    return fil_list


pobj= twit_oauth('xxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
                 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')


# 自分の username を入れて,フォロワーを取得
followers = simplejson.loads(pobj.callAPI('getfollowers','username'))
# フォロワーのプロファイルを取得
profiles  = getProfiles(followers,pobj)
# キーワードマッチでフィルタ対象を取得
fil_list  = filterByKeys(profiles)
# ブロック
for fid in fil_list:
    print pobj.callAPI('block',fid)
# report_spam 自動処理で報告してしまうので,非推奨
# for fid in fil_list:
#    print pobj.callAPI('report_spam',fid)

report_spam を呼んでないのは,巻き添えレポートを防ぐため.
キーワードに絶対の自信があれば,使ってください.
and検索,or検索したい場合は,k.search(prof) の所を工夫してください.
実装済.

twitterのspammerのfollowが多い

級にspammerが来始めたので,なんか炎上してた人をフォローしたせいか,大手画像サイトとかをフォローしたせいだろうなぁ.
で,私のfollowerリストから更に他の人に飛び火すると悪いので(もう遅いかも知れんが…),一旦非公開アカウントにしました.
spammerの自己紹介や行動には,一目見て分かるほど特徴が沢山あるので,非公開にしているうちに,自動blockするスクリプトを作ることにした.
これまでの経験上,誰かが私をfollowするのは,頻度が高くても1人/週程度なので,1週間を目処に解決を目指そう.

前提

  • 今借りているレンタルサバ上でwebに溢れているサンプルコードを動かそうとすると,モジュールが足りない
  • pythonのモジュールは,同じディレクトリに置けば動かんこともないが,twitter関係の操作に必要なモジュールを1から入れようとすると,依存関係のため入れるべきモジュールが多い上,それぞれのimport文に対応するよう配置しないといけないので大変すぎる.
  • 自宅PCは最近は1~2回/週しか立ち上げないので,それではspammerのやりたい放題

つーことで,新たな常時起動しているサバを用意するか,コードをある程度自作する必要があることが分かった.
とか思考を全部書いていくと時間が足りないので結論だけ書く.
Google AppEngineでpythonコードを動かすか,標準モジュールだけでpythonコードを動かすかの両睨みができる方法を取ることにする.
具体的には,標準モジュールだけでTwitter APIを操作できるようにしながらも,GAEの開発環境は用意し呼出し部は2パターン作った.

で,cronを使うよりも,twitterでfollowされる度に飛んでくる通知メールをGMailで受け取って,それをトリガーに動作させてみたいと思い,GAEに挑戦することにした.
追記: GAEだもの,GMail くらい直接触れるでしょ,なんて思い込みがあったが,触れないことが分かった.オーノー

GAEについて

pythonしか試していないのでpythonのことを書く.
Google AppEngine より,登録してSDKを落とす.
Windows版はmsiファイル,Linux版はただのzipファイル.
Win版は,C:\Program Files (x86)\Google\google_appengineへインストールされる.Linux版は,unzipして展開するだけ.
python2.5.x でないと動かない.

$ python (PATH)dev_appserver.py demos/guestbook

と実行すれば,サンプルが動作する.

guestbookと同じように,app.yamlとそこから呼び出されるpyファイルを配置すればOK.
制限をかけたい場合は,app.yamlの制限したいscriptを定義しているscript行の下あたりに,login: required (Googleアカウントでログインしてないとダメ) とか, login: admin (GAEの該当プロジェクトの管理者権限がないとダメ) とか書く.
GAEへアップロードするには,

$ python appcfg.py update project_name

とかする.
アクセス先は GAE の dashboard の application settings にあるが,http://application_name.appspot.com とかだと思う.
サンプルみたいに wsgiref.handlers.CGIHandler().run(application) で呼び出してもいいが,デバッグしたい時は,from google.appengine.ext.webapp import utilとインポートしておいて,util.run_wsgi_app(application)とすると,ブラウザ上にデバッグメッセージが表示される.
どうせ開発環境では,dev_appserver.pyを呼び出している窓にも同じメッセージが表示されるけど.
詳しくは,ユーティリティ関数 - Google App Engine - Google Codeとかを参照のこと.
あと,simplejsonとか,ちょっと欲しいモジュールは入っていたりするけど,oauthtwitterとかニッチなのはない.
外部ファイルからクラスをインポートしたい時は,同じフォルダにpyファイルを置いてimportすれば,開発環境では動く.GAE上では試してないので不明.

twitterAPIについて

仕様がよく変わるので,公式の最新のもの(英語)Documentation | Twitter Developersの REST API を見た方が早い.
BASIC認証はもう使えないらしいので,OAuth認証を使う覚悟をする.
情報取得系はGETで叩き,情報更新系はPOSTで叩くRESTful I/F.
応答はJSONかXML.
アクセストークンの発行が死ぬほど面倒くさいので,頑張る.私は,やる夫と Python で学ぶ Twitter の OAuth - YoshioriのBlogを参考にした.
どうせReadもWriteもやりたくなるので,Read/Writeは少なくともつけておく.
QUERYをトークンをキーに符号化したものを署名としてHTTPヘッダへ埋め込み,肝心のQUERYは上記の通りGETかPOSTで投げるので,二重に必要なことを忘れない.

で,上に記載のやる夫と Python で学ぶ Twitter の OAuth - YoshioriのBlogのソースコードを改変して作ったのがこんなクラス → py_twit.py 更新しました → twit_oauth.py
無駄に書いてるimportとかはそのうち消します.
なお,screen_nameをuser_idとする場合,idは文字列で渡すこと.
また,statuses/updateは同じ発言を2回書くと403を返す,など応答については各々調べること.

あんま関係ないが,pythonのこと調べてると,大学時代の同級生がたいてい出てくるので,よくビビる.
今回の案件では,コードの美しさというよく分からん評価軸について語っていたので,反映しなかった.

weblogの記事をSQL叩いて修正

前のblogが,何故か管理者ページから更新できなくなってしまったので,mysqlを勉強した.

update db_name set key=value where condition1;

where句で指定するエントリの特定キーだけアップデートできるんだ,と分かって安心した.
レンタルサーバ上で日本語の修正をするのは面倒そうだが,公開/非公開とか,日付変更とかなら問題ない.
日本語修正する場合は,ファイルからのリダイレクトでコマンド実行してやれば,なんとかなりそうな気がする.

Yahoo Messenger のメッセージウィンドウにメッセージが表示されない

-- 追記
やっぱし直らなかったので,Yahoo Messenger 9.5 を入れたら直った.
9.5はこちら→ Windows版メッセンジャー ヘルプ - お試し版Yahoo!メッセンジャー バージョン9.5について

--

最近,メッセージウィンドウに表示されるはずの文字が再描画されず,発言があったことを示す窓の点滅と音がすれど,窓は真っ白なままという現象があった.
窓サイズを変更すると,描画コマンドが走るためか,その時点で発言されている文字が表示される.

で,見付けた解決方法はこれ.
Windows版メッセンジャー ヘルプ - メッセージが表示されない

どうやら,IE9にした際に設定が変更されたらしい.

WPtouch

今日,wireless japan 2011の講演で紹介されていたタブレット端末向けプラグイン「WPtouch」を入れてみたら,
案の定,またパースエラーが出た.
時間と元気がないので,バグ取りはまたこんど.

タグを好意的に解釈してくれるブラウザはユーザの味方だが,
開発者はそれに頼ってはならん気がする…
といいつつ,俺自身のミスでないことを祈る.

オンラインにメモっておかないと忘れてしまうので,前職の職場に遊びに行った時に教えてもらったお菓子をメモっておく.
「あべのポテト」と「呼吸チョコ」
一人,天王寺あたりに住んでる友人がいるので,前者については聞いてみるか.