◆ フォームからのデータ処理(839から890行目)
・概要
ついに、フォームからのデータのデコード処理に来ました。ここは重要なので説明内容を濃くします!
CGIの環境変数、URLエンコードのデコード、パールにおける正規表現等です。
・839から890行目
sub decode { if ($ENV{'REQUEST_METHOD'} eq "POST") { if ($ENV{'CONTENT_LENGTH'} > 51200) { &error("投稿量が大きすぎます。","no"); } read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; }
フォームからの要求方法がPOSTで、データ量が51200バイト以下の場合、フォームからのデータ(標準入力)をCONTENT_LENGTH分読み込みます。
フォームからの要求方法がPOST以外(GET)だとQUERY_STRINGからデータを代入します。
このように、フォームからの要求方法はPOSTとGETがあり、POSTは標準入力、GETは環境変数へデータが入って来ます。
また、環境変数は %ENV に設定され、$ENV{'REQUEST_METHOD'}のように参照します。
ここで、CGIで利用可能な環境変数の一覧をご紹介いたします。
変数名 | 意味 |
---|---|
AUTH_TYPE | 認証方式(例えば MD5, Basicなど) |
CONTENT_LENGTH | 標準入力から読み込み可能なデータのバイト数(METHOD=POSTの時) |
CONTENT_TYPE | クライアントから送られてきたデータのタイプ。フォームからMETHOD=POSTで送信した場合は、application/ |
GATEWAY_INTERFACE | ゲートウェイプロトコル名称(例えばCGI/1.1) |
HTTP_ACCEPT | ブラウザがサポートする Content-type: のリスト。すべてを許可する場合、*/* となる。 |
HTTP_FORWARDED | この要求をフォワードしたプロキシサーバーの情報。送信されない場合もある。 |
HTTP_REFERER | ブラウザで直前に参照していたURL。送信されない場合や、たまに、全く別のURLを差していることもある。 |
HTTP_USER_AGENT | ブラウザに関する情報(Mozilla/4.01 [ja] (Win95; I) など) |
HTTP_VIA | この要求を経由したプロキシサーバーのホスト名やWWWのプログラム名。 |
HTTP_X_FORWARDED_FOR | この要求をフォワードしたプロキシサーバーのIPアドレスまたは、イントラネット内のローカルIPアドレス。 |
PATH_INFO | パス情報。たとえば、「cgi-bin/xxx.cgi/taro/xxx.htm」というURLでCGIスクリプトを呼び出した場合、PATH_INFOには「/taro/xxx.htm」が格納される。 |
PATH_TRANSLATED | PATH_INFOで指定したファイルの、サーバー上の絶対パス名。 |
QUERY_STRING | 「http://サーバー名/CGIスクリプト名?データ」というURLを要求した場合のデータ部分。 |
REMOTE_ADDR | クライアントのIPアドレス(例えば10.0.0.1) |
REMOTE_HOST | クライアントのホスト名(例えば abc32.abc.co.jp) |
REMOTE_IDENT | クライアント側のユーザーID |
REMOTE_USER | クライアント側のユーザー名 |
REQUEST_METHOD | METHODで指定したデータ取得手段。GET, POST, HEAD, PUT, DELETE, LINK, UNLINKなどの種類がある。 |
SCRIPT_NAME | CGIスクリプトの名前。 |
SERVER_NAME | サーバー名 |
SERVER_PORT | サーバーのポート番号(例えば80) |
SERVER_PROTOCOL | サーバーのプロトコル名(例えばHTTP/1.0) |
SERVER_SOFTWARE | サーバーのソフトウェア名(例えば NCSA/1.3) |
[出典 とほほのWWW入門]いつもお世話になっています ^^;
@pairs = split(/&/,$buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
フォームから送られたデータは以下のようになっています。
変数名1=データ1&変数名2=データ2&...
ですのでまず、@pairsへ &デリミタで”変数名1=データ1”、”変数名2=データ2” 等を格納します。
つぎに、foreach で @pairs から $pair へ ”変数名1=データ1” を代入して、splitで、変数名1とデータ1を別々に取り出しています。
そして、次の2行が URLエンコードのデコード処理です!
「URLエンコード」とは
A〜Z、a〜z、0〜9、* - . @ _ はそのまま。
半角スペースは + に変換。
その他はすべて % に16進の2桁の文字コード。
たとえば、「あ」0x2422なら %24%22になります。
デコードですので上記の反対をしていきます。$value =~ tr/+/ /;は「+」を半角スペースに戻します。
trは、変換演算子といって、正規表現が使えません。その代わり、処理速度が置換演算sより速いです。
つぎの $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; は、すごく難しいです。 ^^;
ご説明致します。
まず、%([a-fA-F0-9][a-fA-F0-9])は漢字等のエンコード状態で、(〜)による記憶で16進値をあらわす文字列が $1 へ渡されます。
これをhex関数で10進数の数値にし、pack関数でその数値を文字コード1文字に変換します。
結果的に、この1文字へ16進値をあらわす文字列がs///egで変換されるわけです。
g指定で$value全体に適用します。
また、e指定はpack("C", hex($1))を式として評価した結果を対象にすることです。
これがないと”pack("C", hex($1))”の文字列で置換してしまいます。
ここで、正規表現についてご説明しようと思いましたが、次回に譲ります。本人、頭がとんがらがってしまいました。^^;
しかし、正規表現を手足のように使いこなしている人って、どんな頭しているんだろう、尊敬致します。
# タグ処理 if ($tagkey == 0) { $value =~ s/</</g; $value =~ s/>/>/g; $value =~ s/\"/"/g; } else { $value =~ s/<>/<>/g; $value =~ s/<!--(.|\n)*-->//g; }
タグ入力不可指定の場合はタグ効果を無効にしています。
タグ入力可能指定の場合は <>を無効にしています。これは、ログデータのデリミタに<>を使用している為です。
また、s/<!--(.|\n)*-->//g;はSSIを無効にしています。
任意の文字(.)、又は(|)改行(\n)が、0文字以上続く場合は、それを削除しています。
# 削除情報 if ($name eq 'del') { push(@DEL,$value); } $FORM{$name} = $value; } $name = $FORM{'name'}; $com = $FORM{'com'}; $com =~ s/\r\n/<br>/g; $com =~ s/\r/<br>/g; $com =~ s/\n/<br>/g; $email = $FORM{'email'}; $url = $FORM{'url'}; $url =~ s/^http\:\/\///; $mode = $FORM{'mode'}; $pwd = $FORM{'pwd'}; $icon = $FORM{'icon'}; $area = $FORM{'area'}; $page = $FORM{'page'};
ここまでで目新しいのは、push(@DEL,$value);です。
これは、@DELの配列変数の最後のリストへ $valueを追加します。
# 日時の取得 $ENV{'TZ'} = "JST-9"; ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time);
タイムゾーン(TZ)環境変数へ日本時間指定をし、localtime関数で現日時を取得しています。
$yearは2000年では100を返し、$monは月-1を返し、$wdayは0で日曜、6で土曜です。
# 日時のフォーマット @week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); $date = sprintf("%04d/%02d/%02d(%s) %02d:%02d", $year+1900,$mon+1,$mday,$week[$wday],$hour,$min); }
日時のフォーマットをsprintfで行っています。sprintfに関しては他の参考書で勉強してください!
ここでは、YYYY/MM/DD(Sun) HH:mm へフォーマットしています。
◆ HTMLヘッダ、ブラウザ情報からフォーム長を定義、エラー処理(891から946行目)
・概要
HTMLヘッダ、ブラウザ情報からフォーム長を定義、エラー処理です。ここもパール的には目新しいものはないので、軽くスルーします。
・891から946行目
HTMLヘッダ処理(&header)
掲示板のHTMLのヘッダー部を標準出力しています。
コンテントタイプ(Content-type:)出力時は、くれぐれも改行を2個以上出力してください。そうしないとCGIがエラーします。
あとは、スタイルシートでフォントサイズなどを設定し、背景gifがある時ない時のボディータグを決めています。
ブラウザ情報からフォーム長を定義処理(&get_agent)
環境変数(HTTP_USER_AGENT)からブラウザのバージョンをもとめ、フォームのサイズをそれ毎に決めています。
エラー処理(&error)
if ($_[1] eq "lock" && -e $lockfile) { unlink($lockfile); } if (-e $tmpfile) { unlink($tmpfile); } : :
$_[1]はサブルーチンの2番目引数が渡されす。$_[0]は当然1番目引数です。
ロック時エラーでロックファイルが残っている時は、ロックファイルを削除しています。
あとは、一時ファイルが残っている時は削除します。
それから、ブラウザへエラーメッセージ$_[0]を出力しています。