#!/usr/local/bin/perl
#==================================================================
# 名称: WwwCount Ver3.13
# 作者: 杜甫々
# 最新版入手先: http://tohoho.wakusei.ne.jp/wwwsoft.htm
# 取り扱い: フリーソフト。利用/改造/再配布可能。確認メール不要。
# 著作権:Copyright (C) 1996-2002 杜甫々
#==================================================================
#==================================================================
# 使いかた:
#==================================================================
# (書式1) wwwcount.cgi?test
# CGIが使用できるかテストを行う。
#
# (書式2) wwwcount.cgi?text
# カウントアップを行い、カウンタをテキストで表示する。
#
# (書式3) wwwcount.cgi?gif
# カウントアップを行い、カウンタをGIFで表示する。
#
# (書式4) wwwcount.cgi?hide+xxx.gif
# カウントアップを行い、xxx.gifを表示する。
#==================================================================
# 履歴:
#==================================================================
# 日付 Ver
# 1997.03.16 1.00 初版
# 1997.04.10 1.10 シグナル処理の追加
# 1997.04.28 2.00 大幅改造/テスト機能追加/GIF連結機能
# 1997.05.08 2.10 ロックファイルが残ってしまうバグを修正
# 1997.05.11 2.20 %7E や \~ を ~ に変換/自URLは表示しないようにした
# 1997.07.06 2.30 10分以上古いロックファイルを消すようにした
# 1997.09.14 2.40 メールのサブジェクトに前日のアクセス件数を表示
# 1997.10.19 2.50 自己診断機能の強化
# 1998.02.15 2.60 セキュリティ改善。hide+で.gifしか読み取れないようにした。
# 1998.09.20 2.70 SSIで使用する際に text の引数を不要とした
# 1998.11.29 2.80 複数カウンターに対応した
# 1998.12.20 2.81 カウンター破壊対処/SIGPIPE対応
# 1999.01.17 2.91 複数カウンターのバグ対応
# 1999.01.17 2.92 日付ファイルが空だとレポートが送れないバグ対応
# 1999.05.30 3.00 nkfを使用しないようにした
# 1999.06.06 3.01 ロックファイルのパーミッションを755から0755に修正
# 1999.06.27 3.02 同一アドレスチェック機能の初期値をオフに修正
# 1999.10.03 3.03 誤ってロックファイルを消してしまうことがあるバグを修正
# 1999.10.03 3.04 nameオプションのセキュリティホールに対処
# 1999.10.03 3.05 時刻も記録するよう変更
# 1999.12.12 3.06 nameオプションのセキュリティホール対処が誤っていた
# 2000.01.03 3.07 2000年問題対応
# 2000.12.03 3.10 IPアドレスからホスト名への変換機能をサポート
# 2001.05.01 3.11 説明文などの変更
# 2002.03.24 3.13 From: を指定可能に
#==================================================================
# カスタマイズ:
#==================================================================
# ★ このファイルの 1行目の「#!/usr/local/bin/perl」を perl のパス
# 名にあわせて適切に書き換えてください。パス名が分らない場合は、
# プロバイダやサーバー管理者に問い合わせてください。#! の前には
# 空行もスペース文字も入れないようにしてください。(必須)
# ★ Windows NT で IIS を使用する場合は、wwwcount.cgi がインストー
# ルされているフォルダ名を 'C:/HomePage/cgi-bin' などのように指
# 定してください。(必須)
$chdir = 'e:/inetpub/wwwroot/C-Web/cnt';
# ★ SSIのテキストモードで使用する場合は、$mode = "text"; としてく
# ださい。(必須)
$mode = "";
# ★ 表示桁数を例えば5桁に指定する場合は「$figure = 5;」のように指
# 定してください。0 を指定すると桁数自動調整になります。
$figure = 6;
# ★ ファイルロック機能をオンにする場合は 1 を、オフにする場合は 0
# を指定してください。通常は 1 でよいでしょう。
$do_file_lock = 1;
# ★ 同アドレスチェック機能をオンにする場合は 1 を指定してください。
# 同じ日に同じ IP アドレスからのアクセスをカウントアップしなく
# なります。
$do_address_check = 0;
# ★ レポート機能を使う場合は「$mailto = 'abc@xxx.yyy.zzz';」のよう
# に自分のメールアドレスを設定してください。サーバーで sendmail
# コマンドがサポートされている必要があります。
$mailto = '';
# ★ レポート機能の送信元メールアドレス(通常は自分のアドレス)を
# 指定してください。省略時はカウンタ名になりますが、プロバイダ
# によっては、送信元メールアドレスが適切なものかチェックしてい
# るケースがあります。
$mailfrom = '';
# ★ レポート機能で、sendmail コマンドのパス名が /usr/lib/sendmail
# と異なる場合は、適切に修正してください。
$sendmail = '/usr/lib/sendmail';
# ★ レポート機能で、詳細情報を添付せず、アクセス件数のみをレポー
# トする場合は、0 を指定してください。
$account_detail = 1;
# ★ レポート機能で、アクセス元のホスト名を取得できない場合に、は、
# この値を 1 に変更すると、IPアドレスからホスト名への変換を試み
# るようになります。ホスト名変換は、サーバー負荷の原因にもなるの
# でご注意ください。
$do_addr_to_host = 0;
# ★ レポート機能において、「$my_url = 'http://www.yyy.zzz/';」とす
# ると、このアドレスにマッチするサイトからの FROM は表示しなくな
# ります。
$my_url = '';
# ★ 省略時のカウンター名を指定します。カウンター名は *.cnt や *.dat
# などのファイル名に対応しています。
$count_name = "wwwcount";
#==================================================================
# 処理部:
#==================================================================
#
# 関連するファイルを洗い出しておく
# このCGIスクリプトのファイル名の拡張子を変更したものになる。
#
$file_count = "$count_name" . ".cnt";
$file_date = "$count_name" . ".dat";
$file_access = "$count_name" . ".acc";
$file_lock = "lock/$count_name" . ".loc";
#
# CGIが使用できるかテストを行う。
#
if ($ARGV[0] eq "test") {
print "Content-type: text/html\n";
print "\n";
print "\n";
print "
Test\n";
print "\n";
print "OK. CGIスクリプトは動作可能\です。
\n";
if ($mailto ne "") {
if (! -f $sendmail) {
print "ERROR: $sendmail が存在しません。
\n";
}
}
if (-d $file_lock) {
print "ERROR: $file_lock が残っています。
\n";
}
if (! -r $file_count) {
print "ERROR: $file_count が存在しません。
\n";
} elsif (! -w $file_count) {
print "ERROR: $file_count が書き込み可能ではありません。
\n";
}
if (! -r $file_date) {
print "ERROR: $file_date が存在しません。
\n";
} elsif (! -w $file_date) {
print "ERROR: $file_date が書き込み可能ではありません。
\n";
}
if (! -r $file_access) {
print "ERROR: $file_access が存在しません。
\n";
} elsif (! -w $file_access) {
print "ERROR: $file_access が書き込み可能ではありません。
\n";
}
if (($chdir ne "") && (! -d $chdir)) {
print "ERROR: $chdir が存在しません。
\n";
}
print "\n";
print "\n";
exit(0);
}
#
# IIS の場合に、カレントフォルダを変更する。
#
if ($chdir ne "") {
chdir($chdir);
}
#
# 引数を解釈する
#
for ($i = 0; $i <= $#ARGV; $i++) {
if ($ARGV[$i] eq "text") {
$mode = "text";
} elsif ($ARGV[$i] eq "gif") {
$mode = "gif";
} elsif ($ARGV[$i] eq "hide") {
$mode = "hide";
$giffile = $ARGV[++$i];
if (!($giffile =~ /\.gif$/i)) {
exit(1);
}
if ($giffile =~ /[<>|&]/) {
exit(1);
}
} elsif ($ARGV[$i] eq "name") {
$count_name = $ARGV[++$i];
if ($count_name !~ /^[a-zA-Z0-9]+$/) {
exit(1);
}
$file_count = "$count_name" . ".cnt";
$file_date = "$count_name" . ".dat";
$file_access = "$count_name" . ".acc";
} elsif ($ARGV[$i] eq "ref") {
$reffile = $ARGV[++$i];
}
}
#
# 環境変数TZを日本時間に設定する
#
$ENV{'TZ'} = "JST-9";
#
# ロック権を得る
#
if ($do_file_lock) {
foreach $i ( 1, 2, 3, 4, 5, 6 ) {
if (mkdir("$file_lock", 0755)) {
# ロック成功。次の処理へ。
last;
} elsif ($i == 1) {
# 10分以上古いロックファイルは削除する。
($mtime) = (stat($file_lock))[9];
if ($mtime < time() - 600) {
rmdir($file_lock);
}
} elsif ($i < 6) {
# ロック失敗。1秒待って再トライ。
sleep(1);
} else {
# 何度やってもロック失敗。あきらめる。
exit(1);
}
}
}
#
# 途中で終了してもロックファイルが残らないようにする
#
sub sigexit { rmdir($file_lock); exit(0); }
$SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit";
#
# カウンターファイルからカウンター値を読み出す。
#
if (open(IN, "< $file_count")) {
$count = ;
close(IN);
} else {
$count = -1;
}
#
# 日付ファイルから最終アクセス日付を読み出す。
#
if (open(IN, "< $file_date")) {
$date_log = ;
close(IN);
} else {
$date_log = "";
}
#
# 今日の日付を得る
#
($sec, $min, $hour, $mday, $mon, $year) = localtime(time());
$date_now = sprintf("%04d/%02d/%02d", 1900 + $year, $mon + 1, $mday);
$time_now = sprintf("%02d:%02d:%02d", $hour, $min, $sec);
#
# 日付が異なる、つまり、今日初めてのアクセスであれば
#
if ($date_log ne $date_now) {
#
# アクセスログをメールで送信する
#
if ($mailto ne "") {
$tmp_count = 0;
open(IN, "< $file_access");
while () {
if (/^COUNT/) {
$tmp_count++;
}
}
close(IN);
$msg = "";
$msg .= "To: $mailto\n";
if ($mailfrom eq "") {
$msg .= "From: $count_name\n";
} else {
$msg .= "From: $mailfrom\n";
}
$msg .= "Subject: ACCESS $date_log $tmp_count\n";
$msg .= "\n";
if ($account_detail) {
open(IN, "< $file_access");
while () {
$msg .= $_;
}
close(IN);
} else {
$msg .= "Access = $tmp_count\n";
}
open(OUT, "| $sendmail $mailto");
print OUT $msg;
close(OUT);
}
#
# アクセスログを初期化する
#
open(OUT, "> $file_access");
close(OUT);
#
# 今日の日付を日付ログファイルに書き出す
#
open(OUT, "> $file_date");
print(OUT "$date_now");
close(OUT);
}
#
# すでに同アドレスからのアクセスがあればカウントアップしない
#
$count_up = 1;
if ($do_address_check) {
open(IN, "$file_access");
while () {
if ($_ eq "ADDR = [ $ENV{'REMOTE_ADDR'} ]\n") {
$count_up = 0;
last;
}
}
close(IN);
}
#
# カウントアップ処理
#
if (($count >= 0) && ($count_up == 1)) {
#
# カウンタをひとつインクリメントする
#
$count++;
#
# %7E や \~ などを処理する
#
$referer = $ENV{'HTTP_REFERER'};
$referer =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$reffile =~ s/\\//g;
#
# アクセスログを記録する
#
open(OUT, ">> $file_access");
print(OUT "COUNT = [ $count ]\n");
print(OUT "TIME = [ $time_now ]\n");
$addr = $ENV{'REMOTE_ADDR'};
$host = $ENV{'REMOTE_HOST'};
if ($do_addr_to_host && (($host eq "") || ($host eq $addr))) {
$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2);
}
if ($host ne "") {
print(OUT "HOST = [ $host ]\n");
}
print(OUT "ADDR = [ $addr ]\n");
print(OUT "AGENT = [ $ENV{'HTTP_USER_AGENT'} ]\n");
# print(OUT "REFER = [ $referer ]\n");
if ($reffile && (!$my_url || ($reffile !~ /$my_url/))) {
print(OUT "FROM = [ $reffile ]\n");
}
print(OUT "\n");
close(OUT);
#
# カウンタをカウンタファイルに書き戻す
#
if (open(OUT, "> $file_count")) {
print(OUT "$count");
close(OUT);
}
}
#
# CGIスクリプトの結果としてカウンターを書き出す
#
if ($count == -1) {
$count = 0;
}
if ($figure != 0) {
$cntstr = sprintf(sprintf("%%0%dld", $figure), $count);
} else {
$cntstr = sprintf("%ld", $count);
}
if ($mode eq "text") {
printf("Content-type: text/html\n");
printf("\n");
printf("$cntstr\n");
} elsif ($mode eq "gif") {
printf("Content-type: image/gif\n");
printf("\n");
for ($i = 0; $i < length($cntstr); $i++) {
$n = substr($cntstr, $i, 1);
push(@files, "$n.gif");
}
require "gifcat.pl";
binmode(STDOUT);
print &gifcat'gifcat(@files);
} elsif ($mode eq "hide") {
printf("Content-type: image/gif\n");
printf("\n");
$size = -s $giffile;
open(IN, $giffile);
binmode(IN);
binmode(STDOUT);
read(IN, $buf, $size);
print $buf;
close(IN);
}
#
# ロック権を開放する
#
if ($do_file_lock) {
rmdir($file_lock);
}