前回までのおさらい:a リンクで送られたクエリと form から送られたデータは,どうやらエンコードの方針が異なるようなので,デコード方法を区別しないとユーザが望んだ挙動を取る事が出来ません。
とりわけ拙作ブラクラチェッカーの場合,ユーザは URL を入力します。a リンクによるクエリは必要なだけエンコードされているので何もしなくても問題ありません:むしろ何もしない方がよりユーザの望む結果が得られます。しかし form から送られたデータは必要以上にエンコードが行われているため,昨日のコードを用いて,a リンクのクエリのような文字列を得る事が必要です。
さて今日は,昨日のコードを最適化してみます。昨日のコードは,form データを一度ユーザが入力した本来の値にデコードし,そこから一般的なブラウザが a リンクに施すようなエンコードを行う事で a リンクのエンコードをエミュレートしました。これをまとめてみると,つまりこんな事。
'%23' は '#',つまりフラグメントを表すため,以下切り捨てる'+' は切り捨てる'+' は半角空白にデコードせず,直接 '%20' に変換する'%21' 〜 '%7E' のうち,'%22(")','%3C(<)','%3E(>)','%60(`)' 以外の記号をすべてデコードするこんなコードになりました。
$data =~ s/%23.*$//; # フラグメント切捨て
$data =~ s/\++$//; # 末尾の半角空白切捨て
$data =~ s/+/%20/g; # 半角空白を処理
my %not_to_decode = (
'20' => 1, '22' => 1, '3C' => 1,
'3E' => 1, '60' => 1, '7F' => 1,
);
$data =~ s{%([2-7][0-9A-Fa-f])}
{$not_to_decode{uc($1)} ? $& : pack('H2', $1)}ge;
正規表現 [2-7][0-9A-Fa-f] は '21'〜'7E' ではなく '20'〜'7F' を表現します。よって '20' と '7F' もデコード対象ではない事を明示しています。
ここで目標を決定しました:form から送られたデータを,a リンクで送られたクエリと同じになるように加工しよう,とね。そのためには注意深く戦略を練る必要があります。
戦略は大きく分けて「エンコードされたデータを,ユーザが入力した元々の文字列にデコード」「デコードした文字列を,ブラウザが a 要素でするようにエンコード」の 2 つ。これで,form からのデータを a リンクのクエリと同じにする事が出来るはずです。
ユーザが記号,例えば '&' と入力した場合,こちらに渡されたデータは '%2B' とエンコードされています。また半角スペース = ASCII コード 0x20 の場合は特に '+' に置き換えられています。ここでは,よく使用される次のコードをそのまま使って大丈夫。
$data =~ tr/+/ /;
$data =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('H2', $1)/ge;
このコードは Perl で CGI を作る人間にとってのバイブル「Perlメモ」( by 大崎さん ) でも紹介されています。
佳境と書いてクライマックスと読め,です。このあたりが若干奇妙な仕様というか,RFC2396 にもハッキリした事は書かれてありません。
RFC2396 にて URI に使用してよいとされる文字は英数と記号 ; / ? : @ & = + , $ - _ . ! ~ * ' ( ),それにエスケープした文字列 %xx のみ。しかし多くのブラウザは,この他に [ ] { } ^ \,それにエスケープに使われたわけではなさそうな % もエスケープせずに送って来ます。結果,エスケープされる文字は半角スペース 0x20 およびそれよりコードの小さいコントロール文字,< > " ` の 4 つの記号,それに 0x7f 以降の文字コード。
'#' 記号は,それ以降が fragment として処理されるため CGI には送られないという事も重要。ユーザがフォームに '#' を入力した場合,確かに '%23' と送られてきます。しかしここでは a リンクで送られたクエリと同じ文字列を得たいのです。'#' 以下は切り捨ててしまいましょう。
もう一つ重要な事:URL の最後尾の空白は切り捨てられます。以上の事を踏まえたコード:
# ここまでで,$data にはユーザが元々入力した
# 文字列にデコードされたものが入っている
# #fragment を切り落とす
$data =~ s/#.*//;
# 後ろの空白は切り落とす
$data =~ s/\s+$//;
# エンコード
$data =~ s/"/%22/g;
$data =~ s/`/%60/g;
$data =~ s/</%3C/g;
$data =~ s/>/%3E/g;
$data =~ s/([\x00-\x20\x7f-\xff])/sprintf('%%%02X', ord($1))/ge;
前述「Perlメモ」ではエンコードの高速なやり方も紹介されていました。一般に sprintf よりも unpack を使う方が高速です。多くのブラウザの a リンクでは '%8E' のように大文字を使ってエンコードしますが,しかし unpack は小文字でエンコードしたがります。ブラクラチェッカーでなるべく正確,高速にキャッシュを検索するために,この点にも注意を払わなければなりません。
ふう,なかなかの大仕事でした。こんなもんでいかがでしょう?
昨日の結論は,a 要素でリンクされた時のクエリと,form から送られて来たクエリや STDIN の値は異なるって事。
この二者を区別しない事には,ブラクラチェッカーのユーザが,本来どんな URL を調べようとしていたのかを突き止める事が困難です。だとするとチェッカーは間違った URL をチェックしようとし,間違った結果をユーザに知らせてしまう事もあり得ます。というか実際に起こっています。なんとかして a 要素でリンクされた場合とフォームで送られた場合とを区別したいんだけど,可能なのかなあ?
まず便利な事に,a 要素は "bcchecker?query-value" のように必要なクエリのみの記述が可能なのに対して,フォームの値は "key=query-value" のようにキーと値が必ずイコール記号で結び付けられます。
さらに幸いな事に,最もよく外部からブラクラチェッカーを利用するもの = アッhan! bbs ( mono さん原作,bunbun さん改/配布 ) は,デフォルトではチェッカーに対して理想的なマークアップを施してくれています。
よって,クエリがキーに結び付けられているかどうかで,a 要素によるリンクとフォームからの入力を区別する事が出来るかも知れない事が分かりました。( もっとも,これを完全なものにするにはブラクラチェッカーを利用する他のソフトウェアにも協力を仰ぐ必要がありますが。。。)
よく考えれば,a リンクでは大抵の場合 'http://〜' が送られて来るのに対して,フォームからは必ず 'http%3A%2F%2F〜' が送られてきます。記号 ':','/' もエンコードされているんですよね。これで区別すれば簡単でした。
本当に私は思いつきだけで生存している人間だと感じました:なぜかブラクラチェッカーの本格的なバージョンアップに着手しようとしています。期間はわずか 10 日。それを越すと「帰省」というイベントが待っているので,しばらく作業が止まってしまうのです。
現在の問題の一つは「入力された URL の処理について」です。URL の中に記号が含まれている場合,大抵はエンコードされて送られるんだけど,例えば拙作「ブラクラチェッカー」で "http://server.net/?query +%00&#fragment" という URL をチェックしたい場合・・
・マークアップ
<a href="bcchecker?http://server.net/?query +%00&#fragment">check</a>
・bcchecker に送られた $ENV{'QUERY_STRING'} の内容
http://server.net/?query%20+%00&
・マークアップ
<form action="bcchecker"><fieldset>
<input type="text" name="url" />
</fieldset></form>
( フィールドに "http://server.net/?query +%00&#fragment" と入力 )
・bcchecker に送られた $ENV{'QUERY_STRING'} ( GET の場合 )
もしくは STDIN の内容 ( POST の場合 )
url=http%3A%2F%2Fserver.net%2F%3Fquery+%2B%2500%26%23fragment
要するに若干違うという事。a 要素でリンクされたか / フォームから送られて来たかを区別してなるべく統一的な URL の表現を保持しなければ,ブラクラチェッカーのキャッシュ検索の効率は落ちてしまいます。
明日に続けようっと。
make の挙動が謎です。autogen が環境に合わせてきちんと Makefile を作ってくれたものと思っていたのに,なぜか Makefile になさそうなルールでもって player_example.c をコンパイルしようとします。そして theora.h を見つけられずにコンパイルエラー。。。
Makefile のどこかをいじれば良さげなんだけど,一体 make は Makefile のどこを見てあんなコンパイルを行っているかさえ謎なのです。要するに,どこをいじれば良いかすら分からない状態。
これくらいならば自分で Makefile を作ってもよさそうなんだけど,しかしそれは悔しいです。なんとか下賜された Makefile でもってコンパイルできないものかなあ。。。
出典がどこだったか忘れてしまったんだけど,「左辺 演算子 右辺」のような新しい演算子を定義するアイデアですね。
#include <iostream>
struct max_sign { };
template< typename T >
struct max_lefthand {
T& value;
max_lefthand( T& lhs ): value( lhs ) {}
};
// 左辺と演算子を結びつける operator
template< typename T >
const max_lefthand< T >& operator ^ ( T& lhs, const max_sign& )
{ return max_lefthand< T >( lhs ); }
// (左辺 + 演算子) と右辺を結びつける operator
template< typename T >
T operator ^ ( const max_lefthand< T >& lhs, T rhs ) {
if ( lhs.value < rhs ) lhs.value = rhs;
return lhs.value;
}
max_sign max;
///-------- テスト ------------------------------------------------
int main( int ac, char *av[] ) {
// test_int
{
int i = 0;
i ^max^ 10;
i ^max^ 1;
i ^max^ 5;
i ^max^ 13;
i ^max^ 7;
std::cout << i << std::endl;
}
// test_double
{
double d = 0.0;
d ^max^ 0.2;
d ^max^ 1.4;
d ^max^ 2.2;
d ^max^ 0.35;
d ^max^ 2.1;
std::cout << d << std::endl;
}
return 0;
}
たがが最大値の計算でこのアイデアを用いるのはどうかと思いますが,結構いろんな所で応用は利くと思うですよ。既存の,いじる事ができないクラスにこういう演算子を付けたい場合とか。
ちょっと苦しい。。。
少なくとも python がなければ,doc ディレクトリ内の make の途中でエラーが発生し,make がそこでストップします。そこで python をインストールしてみたもののやはり失敗。doc ディレクトリ内は諦める事にしました。
なぜか encoder_example は SDL を利用しています。よって enocder_example.c の main は SDL_main に置き換えられます。しかし Makefile に記述されたルールでは SDL のライブラリファルにリンクしていません。Makefile を次のように書き換えます。
encoder_example_LDADD に $(SDL_LIBS) を追加dump_video_LDADD に $(SDL_LIBS) を追加これで encoder_example までコンパイル終了。さすがにシャレにならないほと大変でした・・・。
はー,なんとか encoder_example のコンパイルまで成功しました。player_example もコンパイルしたかったんだけど,もうちょっと研究が必要な模様。
これらのコンパイルにはインクルードパス,それにライブラリのパスをきちんと設定してやる必要があります。cygwin を起動する前に /etc/profile ファイルの中で環境変数を設定しておくとらくちん。
# ライブラリは PATH を参照する export PATH="/usr/local/mingw32/lib/:$PATH" # インクルードパスは C_INCLUDE_PATH を参照する export C_INCLUDE_PATH="/usr/include/" export C_INCLUDE_PATH="/usr/include/mingw/:$C_INCLUDE_PATH" export C_INCLUDE_PATH="/usr/local/mingw32/include/:$C_INCLUDE_PATH"
私の cygwin と gcc は新しいものだから? ctype.h で _ctype_ という変数が宣言されています。しかしどのライブラリにも実体は定義されていないらしく,リンク時に "undefined reference to `_ctype_'" というエラーとなります。
mingw をインストールすると ( なぜか ) いくつかのパスにヘッダファイルが置かれ,その中に _ctype_ 変数を使わない ctype.h があるはず。そのヘッダファイルが最初に検索されるように,順番を考えて環境変数 C_INCLUDE_PATH を設定する必要があります。
古いバージョンの libogg と libvorbis では ./configure --prefix=/usr/lib/mingw32 としてライブラリを /usr/lib/mingw32 にインストールする事が出来ました。しかし cvs からチェックアウトした nightly ではもはや ./configure ではなく autogen.sh を使います。--prefix オプションは使えるのだろうか?
・・大丈夫。autogen.sh に渡されたオプションは,autogen.sh で生成された configure にそのまま渡されます。あーよかった。
・・よく分かりません。theora のインストールには,ogg や vorbis が生成した *.m4 のようなファイルが必要なようです。それらは・・私の場合は /usr/local/mingw32/share/aclocal にあったので,autogen.sh の時に環境変数 ACLOCAL_FLAGS として設定してやります。
こんなコマンドになりました。
env ACLOCAL_FLAGS="-I /usr/local/ming32/share/aclocal" \ CC="gcc -mno-cygwin" \ sh autogen.sh --prefix=/usr/local/mingw32
make についてはまた明日。
私の WindowsMe では DirectX 9.x にアップグレードしない限りこの脆弱性を fix する事はできません。しかし DirectX 9.x,あまり良い噂は聞きません・・。
そこで,あえてアプしないという選択肢をとってみるテスト。どうせ普段 MIDI を聞くのは Winamp です。信頼できないオンラインゲームで遊ぶ事さえしなければ,多分・・大丈夫・・。
要するに私は configure / make / make install に慣れていなかったわけで,しかし見た目のゴチャゴチャさに惑わされさえしなければ案外簡単である事が分かった・・気がします。まだ theora のインストールには成功していませんが。
以下のメモは 2003-07-24 現在の情報。私はここでつまり「ネット上の情報をそのまま信じるのではなく,多少は自分でも試行錯誤が必要」と言いたい だけ なのでご注意です。
確かに公式サイトからは libvorbis-1.0.tar.gz のように,tar で整理されて gzip で圧縮してくれたようなものがダウンロード可能です。Theora は「vorbis 1.0 以降のものが必要」と言われていますが,libvorbis-1.0.tar.gz のようなものをダウンロード / インストールしても無駄でした。必要な API が揃っていません。Ogg も Vorbis も,CVS から最新の nightly をダウンロード / インストールします。
私は Windows + cygwin,mingw32 モードで theora をインストールしたいと考えています。ここで参考サイトを紹介。say さんの Routine-Work が秀逸。文章量で圧倒されて大変ですが,その中でも ( 私にとって ) 特に重要なリソースはこれ。
cygwin の環境設定,SDL の環境の構築,libogg,libvorbis のインストールまでの手順です。theora をインストールするならばこの中で紹介されている libogg-1.0.tar.gz や libvorbis-1.0.tar.gz をダウンロードするべきではありませんが,基本は同じ。ただし nightly をダウンロードした場合,Makefile を生成するには sh configure ではなく sh autogen.sh の模様。
ここで私の場合,lib*** の Makefile を作る時には環境変数 CFLAGS に -I/usr/local/mingw32/include/ も付け足す必要がありました。libogg のヘッダファイル ogg.h などがここにインストールされるからですね。
さて,明日も元気に格闘です格闘。
現在 cygwin 上にて格闘中。libogg および libvorbis はなんとかインストールできています。当面の問題は theora。
いやその前に vorbis のコンパイルも怪しかったりします。「このシステムでは static link できません」という warning が出てるしね。
それを無視して theora を configure,make すると,encoder_example.c のコンパイル・リンク中に _vorbis_analysis_init() などの関数が軒並み "undefined reference" に。「static link できないよ」というのは,そういう事ですね。
・・てか,なんで?
最近 2ch の web 製作板あたりで話題になっているのは,RFC 1855 "Netiquette Guidelines" にある一節。
4.2 Administrator Guidelines
4.2.1 General Guidelines
- Don't point to other sites without asking first.
事前に尋ねる事なく他のサイトを指してはいけない。
「無断で他サイトにリンクすべきではない」と読めてしまいます。
RFC と言えば,WWW を築くハッカーたちの知恵の集大成。従っておけばわりと幸せになれるはずなのです。・・・そのはずなんだけど,Tim Berners-Lee 氏率いる w3c をはじめ,多くの人が主張するように,この一文は web の存在理由の一つを否定してしまいます。ドパッと文章を書いて参考サイトへのリンクを張って,ネットにアプ,人々の目に触れるまでに 1 日とかからないのが web の良いところ ( のはず )。それなのに,いくつかの許諾を待っている間はその参考サイトへのリンクを張るべきではないとは,実に非合理的ではありませんか。
ctrl+F で "1855" な。やはりこの見解がファイナルアンサーかと思われます。
「リンクを張る自由」 + 「URL 移動によりリンク無効,それに伴い文書の信頼性,新鮮さの低下という責任 ( ペナルティ )」この組み合わせで文書を書かせて欲しいのですよ私は。
"pod2html MyModule.pm --outfile MyModule.html" とすると MyModule.pm ファイルの中の pod 形式のモノが HTML ファイルに変換されて出力されるので非常に便利。Perl モジュールのドキュメントのほぼ全ては,このように作成されています。
ふと,出力された HTML にちょこちょこっと手を加えようと考えました。まず pod2html で HTML を出力し,次にその HTML をいじる Perl スクリプトを起動するようなバッチを作るのです。
しかし普通に .bat ファイルを書くと,なぜか pod2html の次に HTML をいじる Perl コードが起動しません。pod2html のリターンコードが悪さをしているのかなあ? ひとまず pod2html.exe を探す事にしました。
見つかりませんでした。
・・・以上で 20 時間以上経過。私は時間を無駄に費やすのが好き過ぎる。
実は pod2html.bat でした。しかもこれは実質 Perl コード。しかもしかも pod2html はモジュール化されており,Pod::Html モジュールをちょっと呼び出しているだけ。という事は,こんなコードにすれば
my $infile = $ARGV[0];
my $outfile = $infile;
$outfile =~ s/\.pm$/.html/;
use Pod::Html
pod2html( $infile, '--outfile=temp.tmp' );
### 以下,出力された temp.tmp を編集して $outfile に保存するコード
これだけで済んじゃいましたとさ。
今日の結論:Windows にも Perl を標準で載せておくべきマジレス。
CAUTION!... At 7.1 million Scovilles this sauce should not be eaten straight out of the jar. Use only as an additive. HOTTEST KNOWN TO MAN!
警告!!1 710 万スコヴィルのため,瓶から直接飲んではいけません。添加物としてのみ使用して下さい。人類史上最強の辛さ!!123
購入するには「21 歳以上である事,使用する時は必ず人に説明する,料理店を告訴しない」等の規約に同意する必要があるようで,ほぼ危険物のような扱い。日本では 30ml で 13,000円 〜 18,000 円が相場? 他の香辛料に比べてかなりの高値ですが,誰も手を出せず,製造量が少ないからという理由と,かなり少ない量で辛さを楽しめるからという理由が考えられます。
ちなみに現時点で最も辛いとされるソースはこっち。1,300 万スコヴィル。
Screamin Demon's Fiery Foods - Blair's Death Sauce -> CALDERA
( 情報によると同ブレア社の「Blair's 6 AM」 ─ 日本では「ブレア氏の午前 6 時」として流通 ─ もかなりの辛さらしいですが,客観的な数値が見つかりませんでした:「スコヴィル」が客観的な指標かどうかはともかくとして )
辛いものは舌の味覚を感じる器官「味蕾」ではなく,レセプターを通じて痛覚を刺激するとか。程度によっては組織が破壊されるので,取り扱いは慎重でなければなりません。香辛料マニアの間で「激辛」と称されるソースは,直に触ると余裕で火傷するそうです。
遊び感覚で無理に摂取して腹痛を起こし,病院に運ばれるケースが度々報告されています。また過剰な味付けは味覚の低下にも繋がる事が判っているので,( 何でもそうだけど ) 摂取はほどほどにすべきです。
私,今日はジョーク書いてません・・・
なんの脈絡もなく HTTP の GET や HEAD を行うモジュールを作ってみたりしました。
->kmHTTP.pm;(EUC-JP)
->kmHTTP.pm.htm;(EUC-JP)
use kmHTTP;
my ( $head, $body ) = kmHTTP::GET( 'http://some.server.net/' );
甚だ簡単ではありますが,これで $body に欲しいものが入っています。LWP::UserAgent がよく分かんなかった人も,これなら大丈夫!
さあ,サイトをうまく管理するためのスクリプトの準備は整いました。帰省の準備としてはこれで OK です。かかってこいや帰省! おらおらおら! あー!
・・っていうのはどうでもいいけど,2002-07-05 のあたりに似た感じのサブルーチンで遊んでいました。こんなの。
sub for_each {
my ($obj, $proc) = @_;
if ( ref $obj eq 'HASH' ) {
scalar keys %$obj;
while ( my ($k, $v) = each %$obj ) {
&for_each( $v, $proc );
}
}
elsif ( ref $obj eq 'ARRAY' ) {
for my $v ( @$obj ) {
&for_each( $v, $proc );
}
}
elsif ( ref $obj eq 'SCALAR' ) {
&{$proc}( $$obj );
}
else {
&{$proc}( $obj );
}
}
my @data = (
[
'data 1-1',
'data 1-2',
'data 1-3',
],
'data 2',
'data 3',
[
'data 4-1',
'data 4-2',
],
);
&for_each( \@data, sub {
print $_[0], "\n";
});
data 1-1 data 1-2 data 1-3 data 2 data 3 data 4-1 data 4-2
んー,もうちょっと面白みが足りないかなあ。。。