Home / Menu

CGI-Perlの基礎講座

CGI-Perlの基礎講座(p04)

(本ページは、KENT WEBのFantasyボードプログラムをステップ単位に説明しています。)

◆ フォームからのデータ処理(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/x-www-form-urlencoded となる。
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/</&lt;/g;
            $value =~ s/>/&gt;/g;
            $value =~ s/\"/&quot;/g;
        } else {
            $value =~ s/<>/&lt;&gt;/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]を出力しています。

Home / Menu