1

第 1 章 CGIプログラミング
1.1

CGI とはCGI（ Common Gateway Interface ）は、Web サーバ上でプログラムを実行し 、動的に HTML 形式などのデータなどを作成して Web ブラウザに渡すための仕組みである。
Java のアプレットや JavaScript はクライアント（ Web ブラウザが実行されているコンピュータ）側でプログラムが実行されるのに対し 、CGI はサーバ（ HTML の文書が置かれているコンピュータ）側でプログラムが実行されるという違いがある。例えばアクセスカウンタはクライアント上のプログラムだけでは実現できないので CGI が必要になる。
CGI を記述する言語は何でもかまわないわけだが、Perl や C を使うことが多いようである。PHP（ http://www.php.net/ ）のように HTML 中にスクリプトを埋め込むタイプもある。Perl が CGI に広く用いられている理由としては、• インタプリタ方式で実行されるのでコンパイルという作業が不要であること• インタプリタ方式の割に高速であること• 正規表現や連想配列などが容易に扱えること• here ドキュメントという形で文字列をプログラム中に埋め込むことが簡単であることなどが挙げられる。しかし Perl は習得が容易であるとは言えず、デバッグがちょっとした職人芸になる。
C 言語は高速に実行できるが、CGI のプログラムに特に必要となる文字列操作が苦手である。
この演習では CGI の作成に Java 言語を用いる。Java は必ずしも CGI のプログラミングに向いているとは言いきれないところがあるが、アプレットの作成にも用いるため馴染みが深いし 、少なくとも文字列処理は比較的 (?) 得意である。

有用なリンクHTML のまとめ• 初めての ホームページ講座 （ http://www.hajimeteno.ne.jp/ ）• UmeJam HTML Help （ http://www.abacuss.com/ ）Java• Java 2 ドキュメント（ Sun ）（ http://java.sun.com/products/jdk/1.2/ja/docs/ja/ ）• Java 2 ドキュメント（ 香川大学工学部内のコピー）（ http://guppy.eng.kagawa-u.ac.jp/2000/Enshu2/docs/ja/ ）• Java Tips（ http://www.asahi-net.or.jp/~dp8t-asm/java/tips/ ）

2

第1章

1.2

CGI プログラミング

CGI の作成と設置

CGI は、単純に言ってしまえば 、HTML のデータ1 を標準出力に出力するプログラムである。
（入力を受け取る方法は後述する。）拡張子は通常（ Web サーバの設定にもよるが ）.cgi である。
ただし 、単に HTML のデータを出力するだけでは、それを Web ブラウザに渡したときに、ブラウザが HTML のデータであるということが拡張子から判別できない。そこで、必ずデータの種類を表わす文字列（ “Content-type: text/html” ）をデータの先頭に付け加える。
ファイル MyDate.java
import java.io.*;import java.util.*;class MyDate {static public void main(String[] args) {Date d = new Date();System.out.println("Content-type: text/html");
System.out.println (); // 空行を出力
System.out.println("<HTML><HEAD></HEAD><BODY>");
System.out.println(d.toString());
System.out.println("</BODY></HTML>");}}
このプログラムは現在の時刻を表示する。このなかのSystem.out.println("Content-type: text/html");
System.out.println (); // 空行を出力
が、以下に続くデータが HTML のデータであるということをブラウザに伝える役目を持つ。 （この “Content-type: text/html” の次には必ず空行が必要である。）
このソースは次のコマンドでコンパイルする。

javac -encoding SJIS MyDate.java“SJIS” のところは 、コンパイルする Java のソースファイルの日本語コード によって 、SJIS, JIS,（以EUCJIS のいずれかを選択する。自分の使用する日本語コードは何か一つに固定しておくと良い。
下では Shift JIS を使用すると仮定する。）
これで、MyDate.class というファイルができる。周知のように、.class ファイルの中身は Javaの中間言語のコードであり、これを実行するには、java という中間コード の実行系が必要である。
java MyDate.cgi ファイルは直接実行可能である必要があるので、Perl や C の場合のように .class ファイルを単に名前を変えるだけ、というわけにはいかない。ここでは .cgi ファイルは java を起動するシェルスクリプトを用いる。
ファイル MyDate.cgi#!/bin/csh
exec /usr/local/jdk/bin/java MyDate
1

もちろん JPEG や PNG などを出力する CGI があっても構わないが 、この演習では扱わない。

1.2. CGI の作成と設置

3

他の名前のクラスを実行するときは一番最後の MyDate だけを変えれば良い。.cgi ファイルは、実行可能フラグを立てておく。CGI は Web サーバにより実行されるので自分以外のユーザでも実行できるようにしておく必要がある。

chmod a+x MyDate.cgi
参考:
演習ではこのようなシェルスクリプトを簡単に生成し 、モード を変更するための Makefile を用意する。この Makefile があれば 、make MyDate.cgi というコマンドで .cgi ファイルを作成できる。ちなみにこれは自分と拡張子を除いた部分が同じ名前の.class ファイルを実行するシェルスクリプトである。
ファイル Makefile.SUFFIXES:.java .class .cgi

.java.class:javac -encoding SJIS $<.java.cgi:
cat template.txt > $@echo "exec /usr/local/jdk/bin/java $*" >> $@chmod a+x $@template.txt には（ 今のところ）#!/bin/cshの一行だけを書いておく。
これで CGI は完成である。用意したファイル（ MyDate.class とMyDate.cgi ）を Web サーバから見えるところ（設定により異なるが、~/public_html/cgi-bin/など ）に置き、Web ブラウザからアクセスする。
（ ~/public_html/cgi-bin/に置いた場合は、通常 URL は、
http://XXX.YYY.ZZZ.QQQ /~login /cgi-bin/MyDate.cgi になる。ただし 、XXX.YYY.ZZZ.QQQ はWeb サーバを実行しているコンピュータの名前または IP アドレス、login は自分のログ イン名になる。）
問 1.2.1 上の CGI プログラムで時刻によって、ブラウザに表示されるときの色が変わるようにせよ。
例えば 、18 〜 6 時が黒、6 〜 12 時が青、12 〜 18 時が赤など 。
ヒント :• Java: java.util.Date クラスの getHours メソッド
(http://guppy.eng.kagawa-u.ac.jp/2000/Enshu2/docs/ja/api/java/util/Date.html)• HTML: <FONT COLOR="RED"> . . . </FONT>など
問 1.2.2 時刻によって、あるいは乱数によって、ブラウザに表示されるページの背景画像が表示されるたびに変わるようにせよ。
ヒント :• Java: java.util.Random クラス(http://guppy.eng.kagawa-u.ac.jp/2000/Enshu2/docs/ja/api/java/util/Random.html)

4

第1章

CGI プログラミング• HTML: <BODY BACKGROUND=" . . . "> . . . </BODY>など• 素材: http://home.interlink.or.jp/~hrak/yakata/index.html（ 素材の館）
http://wakusei.cplaza.ne.jp/ushikai/（ 牛飼いとアイコンの部屋）

1.3

アクセスカウンタ

アクセスカウンタはもっとも代表的な CGI で、Web ページに対するアクセスの回数を記録し 、表示するものである。アクセスの回数はサーバ上のファイルに記録しておく。
ファイル Counter.javaclass Counter {static public void main(String[] args) {try {File f = new File("counter.txt"); // 本当はフルパスで指定する方が安全
BufferedReader in = new BufferedReader(new FileReader(f)); // 入力用 open
String str = in.readLine();int i = Integer.parseInt(str);
in.close(); // 出力用に open する前に 必ず close しておく。
PrintWriter out = new PrintWriter(new FileWriter(f)); // 出力用に open
out.println(++i);
out.close(); // close を忘れないこと// 文字コードを変換するために System.out の代わりにもちいる
PrintWriter stdout =new PrintWriter(new OutputStreamWriter(System.out, "SJIS"));
stdout.println("Content-type: text/html");stdout.println();stdout.println("<HTML><HEAD></HEAD><BODY>");
stdout.println("あなたは "+i+"番目の来訪者です。");
stdout.println("</BODY></HTML>");
stdout.close(); // close を忘れない} catch (Exception e) {e.printStackTrace();}}}
この例では counter.txt というファイルにアクセス回数を記録している。このファイルは、Counter.cgiと同じディレクトリに置いておく。counter.txt の中身は 1 行のみの数字だけのファイルであり、最初は 0 を書いておく。CGI スクリプトがこの counter.txt の内容を書き換えることができるように、counter.txt は誰でも読み書きが出来るようになっていなくてはならない。

chmod a+w counter.txt
このコマンドを実行すると、counter.txt が誰でも（ CGI プログラムも）読み書きができるようになる。
プログラム中のFile f = new File("counter.txt"); // 本当はフルパスで指定する方が安全
BufferedReader in = new BufferedReader(new FileReader(f));...
in.close();1.4. CGI へのパラメータ渡し（ GET 編）

5は、ファイルから入力するときの常套句で、. . . の部分では、in というオブジェクトに対して、System.in からの入力と同じようにファイルからの入力が可能になる。同様に、PrintWriter out = new PrintWriter(new FileWriter(f));...
out.close();は、ファイルへ出力するときの常套句である。 out というオブジェクトに対して、System.out に対するのと同じ メソッドが使用できる。ここまでの部分で、ファイルから数字を読み込み、一つ増やした数字をファイルに書き込んでいる。
また、以下の部分// 文字コードを変換するために System.out の代わりにもちいる
PrintWriter stdout =new PrintWriter(new OutputStreamWriter(System.out, "SJIS"));は、日本語の文字コード を変換するためのコード である。 Java のプログラムの内部では日本語はUnicode というコード 系で表わされている。これを出力するときに、Shift JIS に変換するためのフィルタをかけるようにする。
（ CGI の場合, 文字コード の扱いが通常と比べてなぜかシビアになるようである。）これに続く部分では System.out の代わりに、このstdout を用いている。このstdout についても最後に close() するのを忘れないようにする。
最後に try 〜catch で全体を囲んでいる。この例では実行時の例外（例えば 、ファイルが見つからなかった・ファイルに書き込みが許可されていなかったなどに起因するエラー）に対しては何も処置を行なわない。もちろん、実際には例外が起きた場所と種類によってきめ細かく処置をする方が良い。
ファイル Counter.cgi は、MyDate.cgi 中の “MyDate” を “Counter” に書き換えればよい。もちろん chmod a+x Counter.cgi しておく。
問 1.3.1 カウンタの値が特別な値（例えば 10 の倍数など ）になったときは、メッセージを変えたり、色を変えたりするように改造せよ。

1.png, 2.png などの名前で数字画像ファイルを用意しておいて、このアクセスカウンタや時刻表示
CGI で 1, 2, と表示する代わりに <IMG SRC="1.png">, <IMG SRC="2.png">などにしておくと、数字を画像で表示するアクセスカウンタ・時刻表示 CGI ができる。
問 1.3.2 数字を画像として表示するアクセスカウンタ・時刻表示 CGI を作成せよ。
参考:

数字画像データ• Digit Mania （ http://www.digitmania.holowww.com ）• Counter Art （ http://www.counterart.com/ ）参考:

画像を生成する CGI• gd （ http://www.boutell.com/gd/ ）1.4

CGI へのパラメータ渡し（ GET 編）

これまで紹介した CGI はブラウザ側から CGI プログラムにデータを渡すことはなかったが、ブラウザから CGI にパラメータを渡して CGI の振舞いを変えることも可能である。

6

第1章

CGI プログラミング

例題: キーワード のハイライト
キーワードをパラメータとして受け取り、特定のファイルを読み込んで、キーワード の部分を色を変えて表示する CGI（ HiLite.cgi ）を作成する。
CGI にパラメータを渡す方法には GET と POST の 2 種類がある。ここでは GET について説明する。
GET で CGI にパラメータを渡すには URL のあとに “?” に続けて文字列を書けば良い。この文字列の部分がパラメータとして CGI に渡される。例えば

http://XXX.YYY.ZZZ.QQQ /~login /cgi-bin/HiLite.cgi?printの、print の部分がパラメータになる。
CGI プログラム中では、このパラメータは QUERY_STRING という環境変数（ OS からプログラムに渡される変数）から受け取ることになる。ただし 、C 言語や Perl の場合には環境変数にアクセスする
ことは簡単であるが 、Java では環境変数をアクセスする方法が用意されていない。このため、javaコマンドを起動するシェルスクリプトで環境変数を読んで、java コマンドに渡してやることにする。
（ 下のスクリプトでは、QUERY_STRING 以外に今後必要となる環境変数をついでに渡している）
ファイル HiLite.cgi#!/bin/csh

set d0=""if ($?REQUEST_METHOD) thenset d0="$d0 -DREQUEST_METHOD=$REQUEST_METHOD"
endif
if ($?CONTENT_LENGTH) thenset d0="$d0 -DCONTENT_LENGTH=$CONTENT_LENGTH"
endif
if ($?QUERY_STRING) thenset d0="$d0 -DQUERY_STRING=$QUERY_STRING"
endif
exec /usr/local/jdk/bin/java HiLite（ 実際に演習で使用するスクリプトでは 、デバッグのためにもう少し 拡張して 、“./HiLite -get
QUERY_STRING ” または “./HiLite -post CONTENT_LENGTH ” のように起動して、環境変数を渡せることができるようになっている。）
参考:
シェルスクリプトを自動生成するための Makefile も次のように変更する。
.SUFFIXES:.java .class .cgi

.java.class:javac -encoding SJIS $<.java.cgi:
cat template.txt > $@echo "exec /usr/local/jdk/bin/java Y=$$d0 $*" >> $@chmod o+x $@また、template.txt は HiLite.cgi の最後の一行を除いたものになる。
このように java コマンドに -D オプションを用いて渡されたパラメータは Java のプログラム中では、System.getProperty() というメソッドで得ることができる。

1.4. CGI へのパラメータ渡し（ GET 編）

7

ファイル HiLite.java
import java.io.*;import java.util.*;class HiLite {// substrReplace の定義はここに入る
static public void main(String[] args) {try {File f = new File("HiLite.java"); // 本当はフルパスで指定する方が安全
String word = System.getProperty("QUERY_STRING");
InputStreamReader fr = new InputStreamReader(new FileInputStream(f), "SJIS");
BufferedReader in = new BufferedReader(fr);
PrintWriter stdout =new PrintWriter(new OutputStreamWriter(System.out, "SJIS"));
stdout.println("Content-type: text/html");stdout.println();stdout.println("<HTML><HEAD></HEAD><BODY>");
stdout.println("<PRE>");
while(true) {String line = in.readLine();if (line==null) break;line = substrReplace(line, "<", "&lt;");line = substrReplace(line, ">", "&gt;");if (word!=null && word.length()!=0) {line = substrReplace(line, word,"<FONT COLOR=Y="REDY=">"+word+"</FONT>");}
stdout.println(line);}
stdout.println("</PRE>");
stdout.println("</BODY></HTML>");
stdout.close();} catch(Exception e) {e.printStackTrace();
}
}
}
結局、

String word = System.getProperty("QUERY_STRING");の部分で、URL の “?” 以降の部分の文字列が変数 word に入ることになる。

line = substrReplace(line, word,"<FONT COLOR=Y="REDY=">"+word+"</FONT>");で、このキーワード を赤色にするように置換している。ここで、substrReplace は文字列中の部
分文字列を別の文字列に置換するメソッドである2 。

static String substrReplace(String str, String str1, String str2) {// str 中に含まれる str1 を str2 に置換する。
2

この程度のメソッドがなぜ java.lang.String クラスに入っていないのかは不思議だ。

8第1章

CGI プログラミング// 例: substrReplace("conversation", "vers", "serv") => "conservation"
int i = str.indexOf(str1);
if (i==-1) {return str;
} else {return str.substring(0, i)+str2+substrReplace(str.substring(i+str1.length()), str1, str2);}}
ところで、表示するテキストの中に “<” や “>” が入っていると、HTML のタグと解釈されてしまって、表示が乱れる。次の部分

line = substrReplace(line, "<", "&lt;");line = substrReplace(line, ">", "&gt;");は、これらを &lt;, &gt; にそれぞれ置き換えている。
結局、このプログラムは HiLite.java のソース自身（これはたまたまで、別のファイルにしてももちろん良い）の中のキーワード を赤色で表示することになる。

http://XXX.YYY.ZZZ.QQQ /~login /cgi-bin/HiLite.cgi?print
だと、print という部分文字列が赤色で表示されることになる。
例題:

テキストの表から HTML の表へ

1 2 3
4 5 6のような plain text の表を<TABLE BORDER><TR><TD>1</TD><TD>2</TD><TD>3</TD></TR><TR><TD>4</TD><TD>5</TD><TD>6</TD></TR></TABLE>のように HTML に変換する CGI プログラムである。 plain text の表が入っているファイル名をCGI の引数として用いる。

1.4. CGI へのパラメータ渡し（ GET 編）
ファイル Tabular.java
import java.io.*;import java.util.*;

9// StringTokenizer

class Tabular {static public void main(String[] args) {try {File f = new File(System.getProperty("QUERY_STRING"));
BufferedReader in = new BufferedReader(new FileReader(f));
PrintWriter stdout =new PrintWriter(new OutputStreamWriter(System.out, "SJIS"));
stdout.println("Content-type: text/html");stdout.println();stdout.println("<HTML><HEAD></HEAD><BODY>");
stdout.println("<TABLE BORDER>");
while(true) {String line = in.readLine();if (line==null) break;
stdout.print("<TR>");
StringTokenizer st = new StringTokenizer(line);
while(st.hasMoreTokens()) {stdout.print("<TD>"+st.nextToken()+"</TD>");}
stdout.println("</TR>");}
stdout.println("</TABLE>");
stdout.println("</BODY></HTML>");
stdout.close();} catch(Exception e) {e.printStackTrace();}}}
http://XXX.YYY.ZZZ.QQQ /~login /cgi-bin/Tabular.cgi?table.txtのように使用する。
このプログラム中の、StringTokenizer クラスについては、http://guppy.eng.kagawa-u.ac.jp/2000/Enshu2/docs/ja/api/java/util/StringTokenizer.html
を参照して欲しい。
注意!

この CGI プログラムを実際に Web サーバに置くことは危険である。

一般に CGI の引数にファイル名を渡す場合には注意が必要である。悪意のある人が ?/etc/passwdのような引数を渡すと簡単にパスワード ファイルを盗んだりすることが可能になる。 CGI の引数にファイル名を許すときは先頭に “/” がないか（つまり絶対パスでないか ）、.. のような文字が入っていないか、をなどをチェックする必要がある。同様に引数として渡された文字列をコマンドとして実行するような CGI も注意が必要である。
自作の CGI を設置する場合はセキュリティに関して細心の注意を払って欲しい。

10

第1章

CGI プログラミング

問 1.4.1 上の例題に、上の注意でファイル名のチェックを追加せよ。また、パラメータが渡されなかった場合（ System.getProperty("QUERY_STRING") が null になる）や、ファイルが存在しなかった場合などのチェックをきちんとするようにせよ。
問 1.4.2 （ カレンダー）
例えば 、Calerdar.cgi?200010 のような形でパラメータを渡されると、2000 年 10 月のカレンダーを作成するような CGI を作成せよ。
ヒント : y 年 m 月 d 日の曜日を求めるのに次のような Zellar の公式を用いよ
static int Zellar(int y, int m, int d) {if (m<3) {// 1 月、2 月は前年の 13 月、14 月として計算する。
y--; m+=12;}
return (y + y/4 - y/100 + y/400 + (13*m+8)/5 + d) % 7;// 0 が日曜、1 が月曜、... 6 が土曜}
問 1.4.3 （ スライドショー）
ディレクトリ中に 1.png, 2.png, . . . のような名前の画像ファイルを用意しておくと、この画像ファイルを順に表示するような CGI（ SlideShow.cgi ）を作成せよ。
ヒント : QUERY_STRING がない（ System.getProperty が null を返す）ときは 1.png を表示する。
QUERY_STRING が n の時は、次のような HTML を生成する。
<HTML><HEAD><TITLE>スライド（ n ）</TITLE></HEAD><BODY><DIV ALIGN="CENTER"><IMG SRC="n .png"><HR><A HREF="SlideShow.cgi?n-1 ">前</A><A HREF="SlideShow.cgi?n+1 ">次</A></DIV></BODY></HTML>問 1.4.4 （ディレクトリ・リスティング ）あるディレクトリ（これは固定で良い）のインデックスを作成する CGI （ DirIndex.cgi ）を作成せよ。ただし 、QUERY_STRING で渡された数の日数より変更された日付が新しいファイルには “NEW!”などのマークをつけるようにせよ。
例えば 、old.txt という 3 日前に変更されたファイルと new.txt という 1 日前に変更されたファイルがあるときは DirIndex.cgi?2 は次のような HTML を出力する。

<HTML><HEAD><TITLE>ディレクトリ</TITLE></HEAD><BODY><UL><LI><A HREF="nex.txt">new.txt</A> NEW!
<LI><A HREF="old.txt">old.txt</A></UL></BODY></HTML>ヒント : java.io.File クラス（ http://guppy.eng.kagawa-u.ac.jp/2000/Enshu2/docs/ja/api/java/io/File.html ）のメソ
ッド、特に list() と lastModified() と java.util.Date クラスを用いる。

キーワード :
CGI, Perl, chmod, GET, 環境変数, QUERY_STRING