←マウントは面倒くさいから amd 編 ↑おもちゃ箱 →JAVA編

¶SSI/CGIのためのテクニカルノート

■ NCSA httpdをテスト用に立ち上げる
■ Apacheをテスト用に立ち上げる
■ SSI/CGIを使うための初期設定
■ SSI(sh)を使ったアクセスカウンタ
■ SSI(perl)を使ったアクセスカウンタ(テキスト版)
■ CGI(perl)を使ったアクセスカウンタ(GIF版)
■ 記帳ページを作る
■ その他のSSI
■ クリッカブルマップを使ってみる
■ WEBページにパスワード認証をつける
■ HTML Helper Mode


━━━━━━━━━━━━━━━━━━━━━━━━━━━━


このページは一般向けのドキュメントではなく、HTMLファイル/CGIスクリプト などを書くための自分用のメモをまとめたものです。利用するのは勝手ですが WEBサーバによっては動作が異なる可能性が有ります。また、SSIとは何か、 CGIとは何かという話しは他のドキュメントやサーバの解説に譲ります。 スクリプト類は、実際に使っているものとは異なることをお断りしておきます。


NCSA httpdをテスト用に立ち上げる


ローカルマシンでテストをしない場合はhttpdのインストール/設定は読み飛 ばしてもいいのですが、せっかくFreeBSDを入れているなら、ローカルマシン上で WEBサーバホストマシンとほぼ同じ環境を作って、手軽にCGIのテストができるの でやってみても損はないと思います。


Apacheをテスト用に立ち上げる


apacheは、NCSA上位互換の機能を持ち、 速くて軽いため人気の高いWWWサーバです。FreeBSDにも portされていますが、 ソースをそのまま持って来ても問題無くコンパイルできます。拡張された機能には 面白いものもあるので、マニュアルをチェックしましょう。


SSI/CGIを使うための初期設定




SSI(sh)を使ったアクセスカウンタ




SSI(perl)を使ったアクセスカウンタ(テキスト版)


あらかじめ、 mkdir ~ryo2/public_html/lock しておきます。
以下のようなperlスクリプトを ~ryo2/public_html/cgi-bin/に置いたとすると
----- count1.pl ------------------------------------------------
#! /usr/local/bin/perl
$cfile = "./count";
$lock = "../tmp/count1.LOCK";
$retry = 5;
while ($retry) {
    if (-e $lock) {
        sleep 1; --$retry;
        next;
    } else {
        symlink("../tmp/.", $lock); # ロックファイル作成
    }
}
exit 0 unless ($retry); # 失敗しても、CGIではエラー終了はしない。

if (open(FP,"$cfile")) {
    $count = <FP>          # ファイル読み込み
    close(FP);
    if (open(FP,">$cfile")) {
        print FP ++$count;         # インクリメントして書き込み
        close (FP);
    }
    print $count;                # ページ出力
}
$HOME/public_html/countは shを使ったアクセスカウンタ同様に作ります。 例えば ~ryo2/public_html/index.htmlに
あなたは<!--#exec cmd="./cgi-bin/count1.pl"-->番目の訪問者です
と書けば、それでカウンタになります。


CGI(perl)を使ったアクセスカウンタ(GIF版)


カウンタをインライン GIF で出力するには、特に RIMNET のように UNIX のシェル環境が使える場合にはアイデア次第でいろいろな方法が 考えられます。 GDtcl なんてのもありますが、個人的には tcl が少々うざったい気がします。 カウンタは C でコンパイルしたプログラムの方が軽くて望ましいのですが、 perl では、状況に応じて短時間に柔軟な処理ができる利点があるので、 ここでは perl4でもできる方法を考えてみます。 perl5 +GD.pm は一番簡単ですが、カウンタだけの目的としてはやや重いことと、 Disk quota の関係で perl5 が使えない状況(それは私)でやる手を考えてみます。 まず並べて表示するのが一番簡単。個々の数字の GIF を並べるので、
----- count2.pl ------------------------------------------------
#! /usr/local/bin/perl
$countfile = "$./count.txt";
$cimage = "gif/num";
die "Cannot open the count file" if (!open(FP,"$countfile"));
$_ = <FP>;
$count = $1 if (/(\d+)/);
close(FP);
if (open (FP,">$countfile")) {
    flock(FP,2);
    print FP ++$count;
    close(FP);
}
@eachstr = split(/ */,$count);
foreach $s (@eachstr) {
    print " <img src =\"cimage$s.gif\">";
}
すべての数字を一つの GIF にする方法を考えてみます。 0〜9の数字の ppmファイルを用意しておき、netpbmのpnmcatを使って、 例えば、カウンタが100になっていたなら、
% pnmcat -lr num1.ppm num0.ppm num9.ppm | ppmtogif
をCGIスクリプトで実行させて Content-type: image/gif として出力すれば、 できます。用意したのは
  ~ryo2/bin
     pnmcat    ppmtogif
  ~ryo2/public_html
     index.html    count.txt
  ~ryo2/public_html/cgi-bin
     counter_gif.cgi
です。pnmcat, ppmtogif, counter_gif.cgi は、当然 chmod a+rw しておかなければ なりません。counter.cgiは後述のようになり、index.htmlでは
あんたは <img src="./cgi-bin/counter.cgi"> 番目のお客だよ
と書くことになります。
----- counter_gif.cgi ------------------------------------------------
#! /usr/local/bin/perl
$userhome = "/home/ryo2";
$rpath = "../";                 # relative path from cgi-bin
$countfile = "${rpath}/count.txt";
$buffer = $ENV{'QUERY_STRING'};
@param = split(/&/,$buffer);
$column = 5			# default column is 5
    unless ($column = shift(@param));
$cimage = "${rpath}gif/num"		# default images is ../gif/num[0-9].ppm
    unless ($cimage = $rpath . shift(@param));
$pnmcat = "${userhome}/bin/pnmcat -lr";
$ppmtogif = "${userhome}/bin/ppmtogif";

print "Content-type: image/gif\n\n";
exit(0)
    if (!open(FP,"$countfile"));
$_ = <FP>;
$count = $1 if (/(\d+)/);
close(FP);
if (open (FP,">$countfile")) {
    flock(FP,2);
    print FP ++$count;
    close(FP);
}
				# complement '0'
@eachstr = split(/ */,$count);
$zeronum = $column - length($count);
for($i = 0; $i < $zeronum ; $i++) {
    unshift(@eachstr, '0');
}

foreach $s (@eachstr) {
    $pnmcat .= " $cimage$s.ppm";
}
$pnmcat .= "|$ppmtogif";
print `$pnmcat`;

このホスト(RIMNET新潟ドメイン)では、CGIの使用メモリ上限が規定されて しまったため、実は、pnmcatが規定に引っかかり動きません。 私は、pnmcatに替わるものを自作しましたが、 リムネットで自力で GIF のカウンタを付けたいという方は、自分で工夫するか、 フリーのwwwcountプログラムを入手して ISP のホスト(リムでは SunOS4.1)上で コンパイルして済ますか、 よそのサービス に頼むという手もあります。

ここで、CGIのお作法を少し、書いておきます。
CGIスクリプトでは引数を環境変数 QUERY_STRING で受け渡しできるので、 このスクリプトでは数字の桁数と数字のppmファイル名を引数としています。
<img src="./cgi-bin/counter.cgi?6">
<img src="./cgi-bin/counter.cgi?6&gif/digit">番目のお客だよ

のような書き方もできて、前者は表示を6桁にして、後者は更に、 gif/digit0.ppm〜gif/digit9.ppmをGIFの材料にしており、CGIに渡される引数は 実際には 5&gif/digitというものになります。
一つ気をつけるのは、SSIと違ってCGIスクリプトが実行されるとき、カレント ディレクトリはスクリプトのあるディレクトリであることです。 だから、~ryo2/public_html/count.txtをアクセスするには、 ~ryo2/public_html/cgi-binにあるスクリプト内では "../count.txt" になります。 上のスクリプトの $rpath はそういう意味です。
また、HTMLファイルに書く CGI スクリプトの在処は、NCSA httpd では、ServerRoot (これはhttpd.confで指定されている)からのディレクトリになるので、ServerRoot が/usr/local/etc/httpdとすると、
<img src="/cgi-bin/foo.cgi"> => /usr/local/etc/httpd/cgi-bin/foo.cgi
<img src="/~ryo2/cgi-bin/foo.cgi"> => $(HOME)/public_html/cgi-bin/foo.cgi
がそれぞれ実行されます。
$(HOME)はユーザryo2のホームディレクトリの意味です。


記帳ページを作る
[工事中]


では、CGIを使った記帳ページを作る事を考えてみましょう。 記帳した直後にすぐ結果が読める方がカッコいいので、それを狙います。

入力フォームからデータ送信

CGIで受けて、データファイルにデータを追加

データを読み出すCGIスクリプトでHTMLの形に出力

というのが普通の形かなと思い、私はそうやってます。しかし、

入力フォームからデータ送信

CGIで受けて、HTMLまたはplain TEXTの形でデータを追加

データを読み出しは、SSIのインクルードを使う

とすればCGIスクリプトが一つで済むので、まずこれを考えてみます。

CGI + SSI の例

これはSSI/CGI両者が使えるホストでないとできません。


その他のSSI


時刻表示の例
<!--#exec cmd="date +DATE:%y/%m/%d"-->
DATE:95/12/18
(%yは年、%mは月、%dは日、%Hは時間、%Mは分、%Sは秒、%Aは曜日)

ページの最終更新時刻表示の例
<!--#config timefmt="%y/%m/%d"-->
<!--#flastmod file="foo.html"-->

%a
曜日
%b
%c
曜日 月 日 時刻 年 (Fri Apr 26 18:29:40 1996)
%d
日(2桁表示)
%e
%h
月(英字)
%j
1月1日から数えた日数
%k
%l
%m
月?
%p
AM / PM
%r
12時間制時刻 (06:29:40 PM)
%s
時刻(Epoch(00:00:00 UTC,January 1,1970)からの秒数)
%u
%v
日-月-年 (26-Apr-1996)
%x
年/月/日 (26/04/96)
%y

別ファイルをインクルードする例
<!--#include file="otherfile.html"-->
<pre><!--#include file="otherfile.txt"--></pre>
Show date
<--#echo var="LAST_MODIFIED"-->


クリッカブルマップを使ってみる


NCSA httpd 1.0a5以降に付属のimagemapプログラムが必要ですので、 自分のホームページがあるWEBサーバ上で環境変数 SERVER_SOFTWARE を確認しましょう。 ただし、最近はクライアントサイドイメージマップの方が速いし、サーバホストへの負荷も軽いのでお勧めです。
マップファイルを作る
xvとエディタがあれば作れますが、手間ですのでmapeditを使っちゃうのが 楽です。まずマップに使う画像ファイルをGIFで用意します。 マップファイルは、~user/public_html/に置くことにしておきます。 仮にmyhome.gifとして、mapeditを立ち上げます。
% mapedit &
FileメニューからOpen/Create Map...を選ぶとOpen Mapウィンドウがでるので、 NCSA/CERNのいずれかを選び、Map Filenameに新しく作るマップファイル名 (仮にmyhome.mapとする)を入れ、GIF Filenameに myhome.gifを入れてOK。 "Map file not found. Create it ?"の問いにOKすると mapeditの画面にmyhome.gifが読み込まれます。試しにRectを選び、 マウスの左ボタンで範囲指定開始、中ボタンで確定(右ボタンでキャンセル) すると、Edit URLウィンドウが出るので、URLを指定します。
この際自分のホームページのどこかを指定するなら、URLを /~ryo2/index.htmlのように書くと、この矩形(rectangle)を クリックしたときに ~ryo2/public_html/index.htmlに飛べるということに なります。Commentの欄は自分が判れば何を書いてもかまいません。 Circle, Polyは同様に使えるでしょう。
マップができたら、 Fileメニューから、Edit Default URL...を選び、デフォルトの飛び先を 書きます。これは、マップ上でPoly/Rect/Circleで指定した以外の領域を クリックしたときにどこへ飛ぶかを意味します。Default URLを書かなかった 場合は、指定した以外の領域をクリックしたときには何も起こらないことに なります。
これでマップは完成。Save Map/Save Map As...のどちらかで セーブします。

HTMLでマップを参照する
HTML(仮にmap.htmlとする)の中では
<a href="/cgi-bin/imagemap/~ryo2/myhome.map">
<img src="gif/myhome.gif" ismap></a>
のように指定します。 この場合各ファイルを置くディレクトリは、それぞれ
myhome.map → ~ryo2/public_html/
myhome.gif → ~ryo2/public_html/gif/
map.html → ~ryo2/public_html/
になるわけです。できれば自分のマシン上でNCSA httpdを 立ち上げて、テストしてみましょう。

【補足】
NCSA httpd Version1.5では、imagemapプログラムの機能がサーバに含まれる ようになったので(Internal Image Map)、

<a href="/~ryo2/myhome.map">
<img src="/~ryo2/myhome.gif" ismap></A>
のようにアンカータグに直接URLを書くことができます。


HTML helper mode for Emacs/Mule


HTML文書を編集するモード。詳しい説明や最新版はサンタフェ大学の HTML helper modeを見て下さい。 html-helper-modeは主なHTML文書を書いているときに挿入するHTMLクッキーの キーバインド群を用意してます。キーシークエンスは対応するクッキーをカー ソルのすぐ右側に挿入します。プレフィックスがある場合はカーソルがダブル クォーテーションに挟まれるようにクッキーがくるむので、文字を挿入してくっ キーを完成することができます。インデンテーション、タイムスタンプ、新ド キュメントのスケルトン生成など沢山の新機能あり。
キー             バインディング
----             --------------
C-c		Prefix Command
RET		newline-and-indent
TAB		html-helper-indent-command
ESC		Prefix Command

C-c RET		tempo-template-html-break
C-c -		tempo-template-html-horizontal-rule
C-c SPC		tempo-template-html-nonbreaking-space
C-c >		tempo-template-html-greater-than
C-c <		tempo-template-html-less-than
C-c &		tempo-template-html-ampersand
C-c TAB		html-helper-image-map
C-c C-f		html-helper-form-map
C-c C-n		html-helper-note-map
C-c C-l		html-helper-list-map
C-c C-p		html-helper-phys-map
C-c C-s		html-helper-logical-map
C-c C-a		html-helper-anchor-map
C-c C-t		html-helper-header-map
C-c C-b		html-helper-head-map

ESC RET		<p>
ESC C-t		html-helper-insert-timestamp-delimiter-at-point
ESC TAB		tempo-complete-tag
ESC C-b		tempo-backward-mark
ESC C-f		tempo-forward-mark

キー               挿入文字
----             --------------
C-c TAB e	<img align="" src="" alt="">
C-c TAB a	<img align="" src="">
C-c TAB t	<img alt="" src="">
C-c TAB i	<img src="">

C-c C-f p	<textarea name="" rows= cols=>  </textarea>
C-c C-f x	<input type="RESET" value="">
C-c C-f b	<input type="SUBMIT" value="">
C-c C-f a	<input type="AUDIO" name="">
C-c C-f s	<input type="SCRIBBLE" name="" size="">
C-c C-f g	<input type="IMAGE" name="" src="">
C-c C-f r	<input type="RADIO" name="">
C-c C-f c	<select name="">  </select>

C-c C-f u	<input type="URL" name="" size="">
C-c C-f d	<input type="DATE" name="" size="">
C-c C-f .	<input type="FLOAT" name="" size="">
C-c C-f i	<input type="INT" name="" size="">
C-c C-f t	<input name="" size="">
C-c C-f f	<form action="">  </form>

C-c C-n m	<margin></margin>
C-c C-n f	<footnote></footnote>
C-c C-n n	<note role=""></note>
C-c C-n a	<abstract>  </abstract>

C-c C-l t	<dt> <dd> 
C-c C-l l	<li> 
C-c C-l d	<dl>  <dt> <dd>   </dl>
C-c C-l m	<menu>  <li>  </menu>
C-c C-l r	<dir>  <li>  </dir>
C-c C-l u	<ul>  <li>  </ul>
C-c C-l o	<ol>  <li>  </ol>
C-c C-l i	<li> 
C-c C-p r	<render tag="" style="">
C-c C-p _	<sub></sub>
C-c C-p ^	<sup></sup>
C-c C-p x	<s></s>
C-c C-p f	<tt></tt>
C-c C-p u	<u></u>
C-c C-p i	<i></i>
C-c C-p b	<b></b>

C-c C-s a	<address></address>
C-c C-s l	<lit></lit>
C-c C-s g	<arg></arg>
C-c C-s m	<cmd></cmd>
C-c C-s .	<abbrev></abbrev>
C-c C-s y	<acronym></acronym>
C-c C-s n	<person></person>
C-c C-s q	<q></q>
C-c C-s d	<dfn></dfn>
C-c C-s v	<var></var>
C-c C-s k	<kbd></kbd>
C-c C-s r	<cite></cite>
C-c C-s x	<samp></samp>
C-c C-s c	<code></code>
C-c C-s s	<strong></strong>
C-c C-s e	<em></em>
C-c C-s b	<blockquote></blockquote>
C-c C-s p	<pre></pre>

C-c C-a l	<a href=""></a>
C-c C-a n	<a name=""></a>

C-c C-t 6	<h6></h6>
C-c C-t 5	<h5></h5>
C-c C-t 4	<h4></h4>
C-c C-t 3	<h3></h3>
C-c C-t 2	<h2></h2>
C-c C-t 1	<h1></h1>

C-c C-b b	<base href="">
C-c C-b l	<link href="">
C-c C-b n	<nextid>
C-c C-b i	<isindex>
C-c C-b t	<title></title>
Written by nelson@reed.edu, http://www.reed.edu/~nelson/


WEBページにパスワード認証をつける
NCSA httpdで特定のディレクトリ以下にアクセス制限をかけたい場合、 以下のようにします。
.htaccessの修正
パスワードをかけたいディレクトリ(仮に /home/ryo2/public_html/private/ とする)にある .htaccess に以下のようなエントリを書き加えます。 なければ .htaccessを作りましょう。
(行末コードはホストに合わせます)
AuthName Arai
AuthType Basic
AuthUserFile /home/arai/public_html/private/.htpasswd
AuthGroupFile /dev/null
<Limit GET POST>
require valid-user
</Limit>
ここで、 % htpasswd -c ./.htaccess ユーザ名
として、パスワードファイル(MD5)を作り、あとは
% htpasswd ./.htaccess 他のユーザ名
として登録していくだけです。

DBMパスワードファイルを使う
NCSA httpd-1.5a以降では、httpd_1.5.XX-export/support/Makefileに、 EXTRA_LIBS = -lndbm を付けてコンパイルするなどして、 dbmpasswd, dbmgroupを使うことができます。 既に flat passwordファイルを作っている場合は、
% std2dbm flat-file dbm-file
としてコンバートできます。