#!/usr/bin/env perl use strict; #================================================================== # 名称: WwwCount 4.0 # 作者: 杜甫々 # 最新版入手先: https://www.tohoho-web.com/wwwsoft.htm # 取り扱い: フリーソフト。利用/改造/再配布可能。確認メール不要。 # 著作権:Copyright (C) 1996-2021 杜甫々 #================================================================== #================================================================== # 使いかた: #================================================================== # (書式1) wwwcount.cgi?test # CGIが使用できるかテストを行う。 # # (書式2) wwwcount.cgi?text # カウントアップを行い、カウンタをテキストで表示する。 # # (書式3) wwwcount.cgi?gif # カウントアップを行い、カウンタをGIFで表示する。 # # (書式4) wwwcount.cgi?hide+xxx.gif # カウントアップを行い、xxx.gifを表示する。 # # (オプション) wwwcount.cgi?(略)+name+counter2 # 複数カウンターを設置する場合にカウンター名を指定する。 # # (オプション) wwwcount.cgi?(略)+ref+xxxxxx # リンク元情報をカウンターに伝える #================================================================== # カスタマイズ: #================================================================== # ★ このファイルの 1行目の「#!/usr/local/bin/perl」を perl のパス # 名にあわせて適切に書き換えてください。パス名が分らない場合は、 # プロバイダやサーバー管理者に問い合わせてください。#! の前には # 空行もスペース文字も入れないようにしてください。(必須) # ★ Windows NT で IIS を使用する場合は、wwwcount.cgi がインストー # ルされているフォルダ名を 'C:/HomePage/cgi-bin' などのように指 # 定してください。(必須) my $g_chdir = ''; # ★ SSIのテキストモードで使用する場合は、$g_mode = "text"; としてく # ださい。(必須) my $g_mode = ""; # ★ 表示桁数を例えば5桁に指定する場合は「$g_figure = 5;」のように指 # 定してください。0 を指定すると桁数自動調整になります。 my $g_figure = 6; # ★ ファイルロック機能をオンにする場合は 1 を、オフにする場合は 0 # を指定してください。通常は 1 でよいでしょう。 my $g_lock_flag = 1; # ★ 同アドレスチェック機能をオンにする場合は 1 を指定してください。 # 同じ日に同じ IP アドレスからのアクセスをカウントアップしなく # なります。 my $g_address_check = 0; # ★ 省略時のカウンター名を指定します。カウンター名は *.cnt や *.dat # などのファイル名に対応しています。 my $g_counter_name = "wwwcount"; # ★ レポート機能を使う場合は「$g_mailto = 'admin@example.com';」の # ように自分のメールアドレスを設定してください。サーバーで # sendmailコマンドがサポートされている必要があります。 # レポート機能を使用しない場合は空文字('')を指定してください。 my $g_mailto = ''; # ★ レポート機能の送信元メールアドレス(通常は自分のアドレス)を # 指定してください。省略時はカウンタ名になりますが、プロバイダ # によっては、送信元メールアドレスが適切なものかチェックしてい # るケースがあります。 my $g_mailfrom = 'admin '; # ★ レポート機能で、sendmail コマンドのパス名が /usr/lib/sendmail # と異なる場合は、適切に修正してください。 my $g_sendmail = '/usr/sbin/sendmail'; # ★ レポート機能で、詳細情報を添付せず、アクセス件数のみをレポー # トする場合は、0 を指定してください。 my $g_report_detail = 1; # ★ レポート機能で、アクセス元のホスト名を取得できない場合に、は、 # この値を 1 に変更すると、IPアドレスからホスト名への変換を試み # るようになります。ただし、ホスト名変換は、サーバー負荷の原因 # になるのでご注意ください。 my $g_addr_to_host = 0; # ★ レポート機能において、「$g_my_url = 'http://www.yyy.zzz/';」とす # ると、このアドレスにマッチするサイトからの FROM は表示しなくな # ります。 my $g_my_url = ''; # ★ レポート機能で、%7E などのエンコード文字をデコードして記録する # 場合は 1 を、そのまま記録する場合は 0 を指定してください。 my $g_decode_url = 1; my $count_dir = $ENV{WWWCOUNT_DIR} // "."; # ★ ロックファイルを作成するフォルダ名を指定します。 my $g_lock_dir = "$count_dir/lock"; #================================================================== # その他変数 #================================================================== # 隠しカウンターで表示するGIFファイル名 my $g_gif_file = ""; # アクセス元情報 my $g_referer = ""; # カウンターファイル名(*.cnt) my $g_file_count = "$count_dir/${g_counter_name}.cnt"; # 最終アクセス日記録ファイル名(*.dat) my $g_file_date = "$count_dir/${g_counter_name}.dat"; # アクセス情報記録ファイル名(*.acc) my $g_file_access = "$count_dir/${g_counter_name}.acc"; # ロックファイル名(*.loc) my $g_file_lock = "${g_lock_dir}/${g_counter_name}.loc"; #================================================================== # 処理部: #================================================================== # # メインルーチン # { my($count, $last_access_date, $now_date, $now_time, $do_countup); # 環境変数TZを日本時間に設定する $ENV{'TZ'} = "JST-9"; # カレントフォルダを変更する。 if ($g_chdir ne "") { chdir($g_chdir); } # 引数を解釈する parseArguments(); # テストモードであればテストを呼び出す if ($g_mode eq "test") { test(); exit(0); } # ロックをかける doLock(); # カウンターを読みだす $count = readCount(); # 最終アクセス日を読みだす $last_access_date = readLastAccessDate(); # 今日の日付と時刻を得る ($now_date, $now_time) = getCurrentDateAndTime(); # 日付が異なる、つまり、今日初めてのアクセスであれば if ($last_access_date ne $now_date) { # レポートメールを送信する sendReportMail($last_access_date); # アクセスログをクリアする clearAccessLog(); # 今日の日付を日付ログファイルに書き出す saveLastAccessDate($now_date); } # 同一IPからのアクセスはカウントアップしないモードの場合、 # カウントアップするか否かを確認する。 $do_countup = checkCountup(); # カウントアップする場合 if ($do_countup) { # カウンターをインクリメントする $count++; # カウンターを記録する saveCount($count); # アクセスログを記録する saveAccessLog($count, $now_time); } # CGIの結果としてカウンターを書き出す outputCounter($count); # ロックを解放する unlockLock(); } # # 引数を解釈する # sub parseArguments { my(@argv) = split(/\+/, $ENV{'QUERY_STRING'}); for (my $i = 0; $i <= $#argv; $i++) { # テストモード if ($argv[$i] eq "test") { $g_mode = "test"; # テキストモード } elsif ($argv[$i] eq "text") { $g_mode = "text"; # GIFモード } elsif ($argv[$i] eq "gif") { $g_mode = "gif"; # 隠しカウンターモード } elsif ($argv[$i] eq "hide") { $g_mode = "hide"; $g_gif_file = $argv[++$i]; if (!($g_gif_file =~ /\.gif$/i)) { exit(1); } if ($g_gif_file =~ /[<>|&]/) { exit(1); } # カウンター名 } elsif ($argv[$i] eq "name") { $g_counter_name = $argv[++$i]; if ($g_counter_name !~ /^[a-zA-Z0-9]+$/) { exit(1); } $g_file_count = "$g_counter_name" . ".cnt"; $g_file_date = "$g_counter_name" . ".dat"; $g_file_access = "$g_counter_name" . ".acc"; $g_file_lock = "$g_lock_dir/$g_counter_name" . ".loc"; # リンク元 } elsif ($argv[$i] eq "ref") { $g_referer = $argv[++$i]; } } } # # カウンターファイルからカウンター値を読み出す。 # sub readCount { my($count) = 0; local(*IN); if (open(IN, "< $g_file_count")) { $count = ; close(IN); } return $count; } # # カウンタをカウンタファイルに書き戻す # sub saveCount { my($count) = @_; if (open(OUT, "> $g_file_count")) { print(OUT "$count"); close(OUT); } } # # 日付ファイルから最終アクセス日付を読み出す。 # sub readLastAccessDate { my $last_access_date; if (open(IN, "< $g_file_date")) { $last_access_date = ; close(IN); } else { $last_access_date = ""; } return $last_access_date; } # # 今日の日付を日付ファイルに書き出す # sub saveLastAccessDate { my($now_date) = @_; open(OUT, "> $g_file_date"); print(OUT "$now_date"); close(OUT); } # # 今日の日付を得る # sub getCurrentDateAndTime { my($sec, $min, $hour, $mday, $mon, $year) = localtime(time()); my($now_date) = sprintf("%04d/%02d/%02d", 1900 + $year, $mon + 1, $mday); my($now_time) = sprintf("%02d:%02d:%02d", $hour, $min, $sec); return $now_date, $now_time; } # # アクセスログを初期化する # sub clearAccessLog { open(OUT, "> $g_file_access"); close(OUT); } # # アクセスログを記録する # sub saveAccessLog { my($count, $now_time) = @_; my($addr, $host, $referer); local(*OUT); open(OUT, ">> $g_file_access"); # カウント print(OUT "COUNT = [ $count ]\n"); # 時刻 print(OUT "TIME = [ $now_time ]\n"); # IPアドレス $addr = $ENV{'REMOTE_ADDR'}; print(OUT "ADDR = [ $addr ]\n"); # ホスト名 $host = $ENV{'REMOTE_HOST'}; if ($g_addr_to_host && (($host eq "") || ($host eq $addr))) { $host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2); } if (($host ne "") && ($host ne $addr)) { print(OUT "HOST = [ $host ]\n"); } # エージェント名 print(OUT "AGENT = [ $ENV{'HTTP_USER_AGENT'} ]\n"); # リンク元(SSI) $referer = $ENV{'HTTP_REFERER'}; if (($g_mode eq "text") && ($referer ne "")) { if ($g_decode_url) { $referer =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; } print(OUT "REFER = [ $referer ]\n"); } # リンク元(CGI) $g_referer =~ s/\\//g; if ($g_referer && (!$g_my_url || ($g_referer !~ /$g_my_url/))) { if ($g_decode_url) { $g_referer =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; } print(OUT "FROM = [ $g_referer ]\n"); } print(OUT "\n"); close(OUT); } # # アクセスログをメールで送信する # sub sendReportMail { my($last_access_date) = @_; my($access_count); local(*IN, *OUT); if ($g_mailto eq "") { return; } # アクセス件数を読み取る open(IN, "< $g_file_access"); $access_count = 0; while () { if (/^COUNT/) { $access_count++; } } close(IN); # レポートメールを送信する open(OUT, "| $g_sendmail -t -i"); print OUT "To: $g_mailto\n"; if ($g_mailfrom eq "") { print OUT "From: $g_counter_name\n"; } else { print OUT "From: $g_mailfrom\n"; } print OUT "Subject: ACCESS $last_access_date $access_count\n"; print OUT "\n"; if ($g_report_detail) { open(IN, "< $g_file_access"); while () { print OUT $_; } close(IN); } else { print OUT "Access = $access_count\n"; } close(OUT); } # # カウントアップするか否かを判断する # すでに同アドレスからのアクセスがあればカウントアップしない # sub checkCountup { my($do_countup) = 1; local(*IN); if ($g_address_check) { open(IN, "$g_file_access"); while () { if ($_ eq "ADDR = [ $ENV{'REMOTE_ADDR'} ]\n") { $do_countup = 0; last; } } close(IN); } return $do_countup; } # # CGIスクリプトの結果としてカウンターを書き出す # sub outputCounter { my($count) = @_; my($count_str, @files, $size, $n, $buf); # カウンター文字列(例:000123)を得る if ($g_figure != 0) { $count_str = sprintf(sprintf("%%0%dld", $g_figure), $count); } else { $count_str = sprintf("%ld", $count); } # テキストモード if ($g_mode eq "text") { printf("Content-type: text/html\n"); printf("\n"); printf("$count_str\n"); # GIFモード } elsif ($g_mode eq "gif") { printf("Content-type: image/gif\n"); printf("\n"); @files = (); for (my $i = 0; $i < length($count_str); $i++) { $n = substr($count_str, $i, 1); push(@files, "$n.gif"); } require "./gifcat.pl"; binmode(STDOUT); print gifcat'gifcat(@files); # 隠しカウンターモード } elsif ($g_mode eq "hide") { printf("Content-type: image/gif\n"); printf("\n"); $size = -s $g_gif_file; open(IN, $g_gif_file); binmode(IN); binmode(STDOUT); read(IN, $buf, $size); print $buf; close(IN); } } # # ロックを得る # sub doLock { my($mtime); if ($g_lock_flag) { for (my $i = 1; $i <= 6; $i++) { if (mkdir("$g_file_lock", 0755)) { # ロック成功。次の処理へ。 last; } elsif ($i == 1) { # 10分以上古いロックファイルは削除する。 ($mtime) = (stat($g_file_lock))[9]; if ($mtime < time() - 600) { rmdir($g_file_lock); } } elsif ($i < 6) { # ロック失敗。1秒待って再トライ。 sleep(1); } else { # 何度やってもロック失敗。あきらめる。 exit(1); } } } # 途中で終了してもロックファイルが残らないようにする sub sigexit { rmdir($g_file_lock); exit(0); } $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit"; } # # ロックを開放する # sub unlockLock { if ($g_lock_flag) { rmdir($g_file_lock); } } # # CGIが使用できるかテストを行う。 # sub test { print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "Test\n"; print "\n"; print "\n"; print "

OK. CGIスクリプトは正常に動いています。

\n"; if ($g_mailto ne "") { if (! -f $g_sendmail) { print "

ERROR: $g_sendmail が存在しません。

\n"; } } if (!-d $g_lock_dir) { print "

ERROR: $g_lock_dir フォルダがありません。

\n"; } if (-d $g_file_lock) { print "

ERROR: $g_file_lock が残っています。

\n"; } if (! -r $g_file_count) { print "

ERROR: $g_file_count が存在しません。

\n"; } elsif (! -w $g_file_count) { print "

ERROR: $g_file_count が書き込み可能ではありません。

\n"; } if (! -r $g_file_date) { print "

ERROR: $g_file_date が存在しません。

\n"; } elsif (! -w $g_file_date) { print "

ERROR: $g_file_date が書き込み可能ではありません。

\n"; } if (! -r $g_file_access) { print "

ERROR: $g_file_access が存在しません。

\n"; } elsif (! -w $g_file_access) { print "

ERROR: $g_file_access が書き込み可能ではありません。

\n"; } if (($g_chdir ne "") && (! -d $g_chdir)) { print "

ERROR: $g_chdir が存在しません。

\n"; } print "\n"; print "\n"; }