www.cgi
- CGIユーティリティ ¶CGIスクリプトを書くのに便利ないくつかの基本的な手続きを提供します。
CGIスクリプトを手軽に書くにはこのモジュールの他に、
rfc.uri
(rfc.uri
- URIの解析と作成)、
text.html-lite
(text.html-lite
- シンプルなHTMLドキュメントの構築)、
text.tree
(text.tree
- 怠惰なテキスト構築) 等のモジュールを併せて
使うとよいでしょう。
注:現在有効な、CGIに関する「正式な」仕様というのはどうも無いようです。 http://w3c.org/CGI/あたりを参照して下さい。
{www.cgi
}
通常、httpdはcgiプログラムに様々な情報を環境変数経由で渡します。
www.cgi
中の多くの手続きはその情報(メタ変数)を参照します。
しかし、cgiに関連するプログラムを開発中に環境変数にアクセスするのは
不便な場合もあります。
このパラメータを使うと、メタ変数をオーバライドすることができます。
Metavariablesは2要素のリストのリストです。 内側のリストは、最初の要素が変数名を、2つめの要素がその値を、それぞれ 文字列で与えます。
例えば次のコードはREQUEST_METHOD
と
QUERY_STRING
のメタ変数をmy-cgi-procedure
の実行期間中に
上書きします。(parameterize
の詳細については
パラメータを参照して下さい)。
(parameterize ((cgi-metavariables '(("REQUEST_METHOD" "GET") ("QUERY_STRING" "x=foo")))) (my-cgi-procedure))
{www.cgi
}
nameで指定されるCGIメタ変数の値を返します。
この関数はまずパラメータcgi-metavariables
を探し、
指定されたメタ変数が見つからなければsys-getenv
を呼びます。
CGIスクリプトは、なるべくsys-getenv
を直接呼ぶのではなく
cgi-get-metavariable
を使うのが良いでしょう。
スクリプトの再利用もしやすくなります。
{www.cgi
}
CGIプログラムに渡されたquery stringをパーズして、パラメータの連想リストにして
返します。文字列がキーワード引数query-stringに与えられればそれがパーズすべき
query stringとなります。その引数が渡されなければこの手続きは
メタ変数REQUEST_METHOD
を参照し、その値によって標準入力もしくは
メタ変数QUERY_STRING
からquery stringが取られます。
そのようなメタ変数が定義されておらず、かつ現在の入力ポートが端末である場合、
インタラクティブにデバッグをしているものと考えて、
この手続きはプロンプトを出してユーザにパラメータの入力を促します。
(単にURLクエリ文字列を分解したいだけなら、rfc.uri
モジュールの
uri-decompose-query
を使うと良いでしょう
(rfc.uri
- URIの解析と作成参照)。
この手続きはCGIスクリプトに便利な機能をたくさん追加しています。)
REQUEST_METHOD
がPOST
の場合、この手続きはenctypeとして
application/x-www-form-urlencoded
とmultipart/form-data
の
両方を処理できます。後者は通常、ファイルアップロード機能を持つフォームに使われます。
POSTデータがmultipart/form-data
で送られて来た場合、
各パートの内容がパラメータの値となります。すなわち、アップロードされた
ファイルはその内容がひとつの文字列として得られることになります。
元のファイル名のようなその他の情報は捨てられます。これが望ましい動作で
ない場合は、part-handlers引数によって動作をカスタマイズすることができます。
詳しくは下の「ファイルアップロードの処理」で説明します。
キーワード引数merge-cookiesに真の値が与えられた場合は、
メタ変数HTTP_COOKIE
からクッキーの値が読まれ、解析されて
結果に追加されます。
パラメータは複数の値を取り得るため、結果のパラメータに対応する値は常にリストになります。
パラメータに値が与えられていなければ、結果のパラメータに対する値には#t
が置かれます。
次の例を参照して下さい。
(cgi-parse-parameters :query-string "foo=123&bar=%22%3f%3f%22&bar=zz&buzz") ⇒ (("foo" "123") ("bar "\"??\"" "zz") ("buzz" #t))
{www.cgi
}
cgi-parse-parameters
が返す、パーズされたQuery文字列paramsから、
名前nameを持つパラメータの値を簡単に取り出すための手続きです。
nameは文字列です。
キーワード引数listに真の値が与えられていなければ、 返される値はスカラー値です。パラメータnameに複数の値が与えられた場合でも、 最初の値のみが返されます。listに真の値が与えられれば、返されるのは 常に値のリストとなります。
キーワード引数convertに手続きを与えると、対応する値が取り出された後でその 手続きが値を引数として呼ばれます。これによって値を文字列から必要な型へと変換することが できます。listに真の値が与えられている場合、変換手続きは各値に対して呼ばれ、 その結果のリストがcgi-get-parameterから返されます。
パラメータnameがQuery中に現れなかった場合は、
defaultに与えられた値がそのまま
返されます。defaultが省略された場合、listが偽であれば#f
が、
真であれば()
が返されます。
{www.cgi
}
HTTPリプライメッセージのヘッダを、テキストツリー形式(text.tree
- 怠惰なテキスト構築参照)
で作成して返します。最も簡単な呼び出しでは次のようになります。
(tree->string (cgi-header)) ⇒ "Content-type: text/html\r\n\r\n"
キーワード引数content-typeによってContent typeを指定できます。
また、cookiesにクッキー文字列のリストを渡すことにより、
クライアントにクッキーを設定できます。クッキー文字列を構築するには手続き
construct-cookie-string
(rfc.cookie
- HTTPクッキー参照)
が使えます。
キーワード引数locationは、Location
ヘッダを作成して
クライアントを別のURIに誘導するのに使えます。また、Status
ヘッダを
指定するためにstatusキーワード引数が使えます。クライアントを
別URIに転送するよくある方法は次のようなものです。
(cgi-header :status "302 Moved Temporarily" :location target-uri)
{www.cgi
}
このパラメータの値は次に説明するcgi-main
が出力するデータの
文字符合化法(CES)を指定します。デフォルトの値はGaucheのネイティブエンコーディング
です。それ以外の値がセットされている場合、cgi-main
は
gauche.charconv
モジュールを用いて出力のエンコーディングの変換を
行います。
(gauche.charconv
- 文字コード変換参照)。
{www.cgi
}
CGIスクリプトのための便利なラッパー手続きです。
この手続きは、まずcgi-parse-parameters
を呼び出してCGIスクリプトに
渡されたパラメータを解析し、続いてその結果を引数としてprocを呼び出します。
キーワード引数merge-cookiesは、与えられればそのまま
cgi-parse-parameters
に渡されます。
手続きprocはHTTPヘッダを含むドキュメントを
テキストツリー構造(text.tree
- 怠惰なテキスト構築参照)で
返さなければなりません。cgi-main
はそれをwrite-tree
を使って
現在の出力ポートに書き出し、0を返します。
もしproc内でエラーが起こった場合、そのエラーは捕捉されて、エラーを報告する
HTMLページが作成されて出力されます。このエラーページは、on-errorキーワード引数に
手続きを渡すことでカスタマイズできます。on-errorに渡された手続きは
エラー発生時に<condition>
オブジェクト(コンディション参照)
を引数として呼ばれ、HTTPヘッダを含むドキュメントをテキストツリー構造で返さねばなりません。
cgi-main
は最終的な結果を出力を書き出す時に
パラメータcgi-output-character-encoding
を参照し、
必要ならば出力の文字エンコーディングを変換します。
cgi-main
の出力のふるまいはキーワード引数output-procで
カスタマイズできます。output-procが渡された場合、それは
procの戻り値、あるいはエラーハンドラが作成したテキストツリー構造を
受け取る手続きでなければなりません。その手続きはテキストツリーを
フォーマットして現在の出力ポートに出力しなければなりません。
必要ならば文字エンコーディングの変換もその手続き内で行います。
キーワード引数part-handlersは、そのままcgi-parse-parameters
に渡されます。この引数によって、ファイルアップロードの際の動作をカスタマイズ
できます。詳しくは下の「ファイルアップロードの処理」の項を参照して下さい。
この引数で、一時ファイルを使うように指定した場合、cgi-main
は
procから抜ける際に(エラーでも正常終了でも)一時ファイルを
消去します。この機能を他でも利用するにはcgi-add-temporary-file
の項を
参考にして下さい。
procを呼ぶ前に、cgi-main
はカレントエラーポートの
バッファリングモードを:line
に変更します。
(バッファリングモードの詳細についてはポート共通の操作の
port-buffering
の項を参照してください)。
これはwebサーバがcgiスクリプトのエラー出力を捕捉しやすくするためです。
以下の例はCGIに渡されたパラメータ全てをテーブルにして表示します。
#!/usr/local/bin/gosh (use text.html-lite) (use www.cgi) (define (main args) (cgi-main (lambda (params) `(,(cgi-header) ,(html-doctype) ,(html:html (html:head (html:title "Example")) (html:body (html:table :border 1 (html:tr (html:th "Name") (html:th "Value")) (map (lambda (p) (html:tr (html:td (html-escape-string (car p))) (html:td (html-escape-string (x->string (cdr p)))))) params)))) ))))
{www.cgi
}
この手続きはcgi-main
に渡されるproc中で呼ばれることを
想定しています。
この手続きは、filenameを一時ファイルとして登録し、procが
終了する際に消去されるようにします。cgiスクリプトがエラー終了した場合
などでもごみを残さないようにする便利な方法です。
この手続きを呼んだ後で、procがfilenameを消去したり
名前を変えたりしても構いません。
{www.cgi
}
cgi-add-temporary-file
で登録された一時ファイルを保持するパラメータです。
cgi-parse-parameters
の項で説明したように、ファイルアップロードは
デフォルトでは透過的に扱われます。すなわち、アップロードされた
ファイルの内容がパラメータの値となります。
これは望みの動作ではないかもしれません。例えばアップロードされるファイルが
巨大であることが予想されるなら、それを全てメモリに読み込んで持ち回りたくは
ないかもしれません。cgi-parse-parameters
やcgi-main
の
part-handlers引数によって、ファイルアップロードの
処理をカスタマイズすることが可能です。
(この引数は、フォームデータがmultipart/form-data
enctypeで
送られた場合にのみ意味を持ちます)。
part-handlers引数が与えられている場合、それはリストのリストで、
内部のリストは(name-pattern action kv-list …)
の形式で
なければなりません。
アップロードされたファイルは、そのパラメータ名がname-patternに
マッチした場合にactionで指示されるように処理されます。
(ここで、パラメータ名とはsubmitされたフォームのinput
要素に与えられた
’name’属性のことです。アップロードされたファイルの名前ではありません)。
name-patternは文字列のリストか、正規表現か、#t
です。
文字列のリストの場合はそれのいずれかとパラメータ名が等しければマッチと
みなされます。#t
は全てのものにマッチします。
action
は次のいずれかの値でなければなりません。
#f
デフォルトのアクションです。すなわち、アップロードされたファイルの内容が 文字列として読み込まれ、パラメータの値となります。
ignore
アップロードされたファイルの内容を無視します。
file
アップロードされたファイルの内容は一時ファイルへと格納されます。 パラメータの値は、一時ファイルの名前となります。
このアクションを使う場合は、エントリを
(name-pattern file prefix)
のように書くことも
でき、その場合はprefixが一時ファイルのパス名のプリフィクスとして
使われます。例えば("image" file "/var/mycgi/incoming/img")
のようにしておくと、"image"
パラメータとしてアップロードされた
ファイルが/var/mycgi/incoming/img49g2Uaのような一時ファイルに
格納されることになります。
アプリケーションは、この一時ファイルを(必要ならば)適切な場所に
移動しなければなりません。cgi-main
を用いている場合は、
一時ファイルはcgi-main
を抜ける際に(まだあれば)unlinkされます。
file+name
file
と同様ですが、パラメータの値が一時ファイル名と
クライアントが送ってきたファイル名からなるリストになります。
クライアントが送信したファイル名を利用したい場合に便利です
(ただ、クライアントが常に正しいファイル名を送って来ると仮定しては
いけません。例えば、アップロードされたファイルを
チェック無しにクライアントが送ってきた名前にrenameするというような
ことは避けてください)。
procedure
この場合、アップロードされた内容を処理するために、手続きprocedureが
呼ばれます。手続きは4つの引数を伴って呼ばれます:
(procedure name filename part-info iport)
.
nameはパラメータの名前、filenameはオリジナルファイルの名前
(クライアント側でのパス名)です。part-infoは<mime-part>
オブジェクトで、
このMIMEパートの情報を保持しており、そしてiportは内容を読むための入力ポートです。
これらの引数の詳しい意味についてはrfc.mime
- MIMEメッセージ処理を
参照して下さい。独自のprocedureを書く際に、rfc.mime
の
mime-retrieve-body
のような手続きが使えるかもしれません。
procedure内で一時ファイルを作る場合は、それを
cgi-add-temporary-file
で登録しておけば、cgi処理中に
エラーが起きた場合でも一時ファイルが消去されるようにすることができます。
actionの後ろにkv-listが与えられた場合、それは キーワード-値リストでなければなりません。次のキーワードがサポートされています。
:prefix
actionがfile
かfile+name
の時のみ有効です。
一時ファイルのプリフィクスを指定します。例えば:prefix "/tmp/foo"
を
与えると、ファイルは/tmp/fooxAgjeQのような名前でセーブされます。
:mode
actionがfile
かfile+name
の時のみ有効です。
一時ファイルのモードをunix式の整数で指定します。デフォルトは#o600
です。
ファイルアップロード以外のパラメータはpart-handlersの対象外である ことに注意して下さい。それらのパラメータの値は常に文字列へと変換されます。
簡単な例を示します。例えば次のようなフォームがあったとします。
<form enctype="multipart/form-data" method="POST" action="mycgi.cgi"> <input type="file" name="imagefile" /> <input type="text" name="description" /> <input type="hidden" name="mode" value="normal" /> </form>
mycgi.cgi内で、cgi-parse-parameters
を
part-handlers引数なしで使った場合は、
例えば次のようなリストがパラメータパージングの結果として得られるでしょう。
(実際の値は、webクライアントがどのようにフォームを埋めたかに依存します)。
(("imagefile" #**".....(image file content as a string)....") ("description" "my image") ("mode" "normal"))
ここでもし、'(("imagefile" file :prefix "/tmp/mycgi"))
を
part-handlersに
渡したなら、替わりに次のような結果が得られるでしょう。
ここで、アップロードされたファイルは/tmp/mycgi7gq0Bにセーブ
されていることになります。
(("imagefile" "/tmp/mycgi7gq0B") ("description" "my image") ("mode" "normal"))
上の例でシンボルfile
のかわりにfile+name
を使えば、
例えば"imagefile"
の値として("/tmp/mycgi7gq0B" "logo.jpg")
のようなものが得られるでしょう。ここで"logo.jpg"
は
アップロードされたファイルのクライアント側でのパス名です。
(注意:クライアントは任意の文字列をファイル名として送信することが
できるため、その文字列が有効なパス名であることを仮定してはなりません。)