#!/usr/bin/perl # ↑この行をプロバイダの環境にあわせて変更してください。 # (上はBIGLOBE 2*サーバー用です) # #! の前には空行もスペース文字も入れないようにしてください。 # perl ver.5 only #================================================================== # 名称: whcount.cgi Ver3.07 rm.5.1 # 原作者: とほほさん 改修: Hassy # 原作入手先: http://tohoho.wakusei.ne.jp/ # 連絡先: hassym@pal.tok2.com # 取り扱い: フリーソフト。利用/改造/再配布可能。確認メール不要。 # # このスクリプトはとほほさんの保証対象外となっていますので # ご質問がある時はhassyの方へお願いします # #================================================================== #================================================================== # 使いかた: #================================================================== # (書式1) whcount.cgi?test # CGIが使用できるかテストを行う。 # # (書式2) whcount.cgi?text # カウントアップを行い、カウンタをテキストで表示する。 # # (書式3) whcount.cgi?gif # カウントアップを行い、カウンタをGIFで表示する。 # # (書式4) whcount.cgi?hide+xxx.gif # カウントアップを行い、xxx.gifを表示する。 # # (書式5) whcount.cgi?flash (Flashからの呼び出しのみ) # カウントアップを行い、カウンタ値をFlashに渡す。 #================================================================== # 履歴: #================================================================== # 日付 Ver # 2000.08.10 rm.1 Flash対応とカウンターイベントの組み込み # 2000.08.15 rm.2 カウンター値喪失時の対処追加 # 2000.10.19 rm.3 アクセスログをサーバーに残す設定を追加 # 2000.12.05 rm.4 次の切り番表示機能と音の再生機能追加 # 2000.12.10 rm.4.1 CGIの高速化 # 2001. 2. 8 rm.4.2 WAKWAKへの対応と細かなバグフィックス # 2001. 4.19 rm.4.3 NIFTYへの対応 # 2002. 5.25 rm.5.0 SSI表示時の切り番表示切替機能追加 #          (上手く作動するのか不安?) #          及びTripodへの対応 # 2002. 8. 1 rm.5.1 ホスト名取得部のバグ修正 # #================================================================== # カスタマイズ: #================================================================== # # SSIのテキストモードで使用する場合は「$mode = "text";」としてください。 # $mode = ""; # # 表示桁数を例えば5桁に指定する場合は「$figure = 5;」のように指定する。 # 自動調整するには「$figure = 0;」と指定する。 # $figure = 5; # # ファイルロック機能をオンにする場合 # サーバーがflock関数をサポートしている時は「$do_file_lock = 1;」とする。 #      〃    サポートしていない時は「$do_file_lock = 2;」とする。 # # ファイルロック機能をオフにする場合は「$do_file_lock = 0;」とする。 # $do_file_lock = 1; # # 同アドレスチェック機能をオンにする場合は「$do_address_check = 1;」とする。 # 同アドレスチェック機能をオフにする場合は「$do_address_check = 0;」とする。 # $do_address_check = 1; # # レポート機能を使う場合は「$mail_to = 'abc@xxx.yyy.zzz';」のように自分の # メールアドレスを設定する。また、sendmailが使用可能であることを確認 # しておく。別の場所にある場合は、$sendmail も適切に変更する。 # 詳細メールの場合は「$account_detail = 1;」とし # アクセス件数のみをメールする場合は「$account_detail = 0;」とする。 # # 但し、アクセス件数のみのメールの場合でもカウンターイベントが起こった # 時のログは送られてきます # # レポート機能を使用しない設定の時は # 自動的にサーバーにログを残す設定になります # $mail_to = ''; $sendmail = '/usr/sbin/sendmail'; $account_detail = 0; # # [サーバーにログを残す設定のみ有効] # サーバーにログを残す日数を設定してください # 「$left_day = 0;」とするとログを自動削除しません # $left_day = 7; # #[サーバーにログを残す設定のみ有効] #ログファイルのパーミッションを設定してください # $pm = "0666"; # # ホスト名の取得をしない場合は # 「$get_hostname = 0;」として下さい。 # (ホスト名の検索が出来ないサーバを使用している場合は #  取得をしない設定にして下さい) # $get_hostname = 1; # # このアドレスにマッチするサイトからの FROM は表示しない。 # $my_url = ''; # # カウンター名 # $count_name = "whcount"; # # [引数にtext指定時のみ有効] # 切り番取得時に表示されるメッセージを # 「$ssi_msg = <<"HTML";」と「HTML」の間に記入してください。 # その時に上記の行は変更しないで下さい。 # (「_cnt_」の部分がカウント値に置き換わります) # # 「$ssi_msg = "";」の1行のみとすると表示は変わりません。 # $ssi_msg = <<"HTML"; conglaturation!! あなたは見事_cnt_をGETしました!!
welcome myBBS!
HTML # # [引数にgif指定時のみ有効] # 今日及び昨日のカウンター値用のgifファイルが在るディレクトリ名 # (積算値と同一にする場合は「$img_dir = "";」 とする) # $img_dir = "small"; # # [引数にgif指定時のみ有効] # カウンターイベントが起きた際のカウンター値の前に表示 # されるgif画像を”〜”内に入力して下さい # 表示したくない場合は「$topgif = "";」とします # $topgif = "./top.gif"; # # [引数にgif指定時のみ有効] # カウンターイベントが起きた際のカウンター値の後ろに表示 # されるgif画像を”〜”内に入力して下さい # 表示したくない場合は「$lastgif = "";」とします # $lastgif = "./last.gif"; # # [引数にsound指定時のみ有効] # 通常時に流れる音楽ファイル # $normal_sound = "./none.mid"; # # [引数にsound指定時のみ有効] # イベント時に流れる音楽ファイル # $event_sound = "./event.mid"; #================================================================== # 処理部: #================================================================== # # 関連するファイルを洗い出しておく # このCGIスクリプトのファイル名の拡張子を変更したものになる。 # $file_count = "./$count_name.cnt"; $file_date = "./$count_name.dat"; $file_access = "./$count_name.acc"; $file_event = "./$count_name.evt"; $file_evtlog = "./$count_name/eventlog.log"; $file_lock_m = "./lock/$count_name.mlo"; $file_lock_f = "./lock/$count_name.flo"; @in = (@ARGV) ? @ARGV : split(/\+/, $ENV{'QUERY_STRING'}); # # CGIが使用できるかテストを行う。 # if ($in[0] eq "test") { print <<"HTML"; Content-type: text/html Test OK. CGIスクリプトは正常に呼び出されました
HTML if ($mail_to ne "") { unless (-f $sendmail) { print "
NG. $sendmail が存在しません。\n"; } } if (-d $file_lock_m) { print "
NG. $file_lock_m が残っています。\n"; } if (! -r $file_count) { print "
NG. $file_count が存在しません。\n"; } elsif (! -w $file_count) { print "
NG. $file_count が書き込み可能\ではありません。\n"; } if (! -r $file_date) { print "
NG. $file_date が存在しません。\n"; } elsif (! -w $file_date) { print "
NG. $file_date が書き込み可能\ではありません。\n"; } if (! -r $file_access) { print "
NG. $file_access が存在しません。\n"; } elsif (! -w $file_access) { print "
NG. $file_access が書き込み可能\ではありません。\n"; } if (! -r $file_event) { print "
NG. $file_event が存在しません。\n"; } elsif (! -w $file_event) { print "
NG. $file_event が書き込み可能\ではありません。\n"; } if (! -r $file_evtlog) { print "
NG. $file_evtlog が存在しません。\n"; } elsif (! -w $file_evtlog) { print "
NG. $file_evtlog が書き込み可能\ではありません。\n"; } if (! -r $file_lock_f) { print "
NG. $file_lock_f が存在しません。\n"; } print "\n\n"; exit(0); } # # 引数を解釈する # $tm = time(); $ref_url = ""; foreach $i (0 .. $#in) { if (($in[$i] eq "text") or ($in[$i] eq "gif")) { $mode = $in[$i]; $cnt_mode = $in[++$i] if (($in[$i+1] eq "today") or ($in[$i+1] eq "yesterday") or ($in[$i+1] eq "next")); } elsif ($in[$i] eq "hide") { $mode = "hide"; $giffile = $in[++$i]; ($giffile =~ /\.gif$/i) or die "Bad giffile in $giffile"; ($giffile !~ /[<>|&]/) or die "Bad giffile in $giffile"; } elsif ($in[$i] eq "name") { $count_name = $in[++$i]; ($count_name =~ /^[a-z\d]+$/i) or die "Bad cntname in $count_name"; $file_count = "./$count_name.cnt"; $file_date = "./$count_name.dat"; $file_access = "./$count_name.acc"; $file_event = "./$count_name.evt"; $file_evtlog = "./$count_name/eventlog.log"; } elsif ($in[$i] eq "ref") { $ref_url = $in[++$i]; } elsif ($in[$i] eq "flash") { $mode = "flash"; $cnt_mode = ""; }elsif ($in[$i] eq "sound") { $mode = "sound"; $cnt_mode = ""; } } sleep(3) if ($cnt_mode or ($mode eq "sound")); # # flock関数でロック権を得る # if ($do_file_lock == 1) { if (open(LOCK, "$file_lock_f")) { eval { flock(LOCK, 2); }; $do_file_lock = $@ ? 2 : 1; } else { $do_file_lock = 2; } } # # ディレクトリ作成でロック権を得る # if ($do_file_lock == 2) { foreach $i (1 .. 10) { if (mkdir("$file_lock_m", 0755)) { # ロック成功。次の処理へ。 last; } elsif ($i == 1) { # 10分以上古いロックファイルは削除する。 ($lock_time) = (stat($file_lock_m))[9]; rmdir($file_lock_m) if ($lock_time < ($tm - 600)); } elsif ($i < 10) { # ロック失敗。1秒待って再トライ。 sleep(1); } else { # 何度やってもロック失敗。あきらめる。 die "filelock failed"; } } # # 途中で終了してもロックファイルが残らないようにする # sub sigexit { rmdir($file_lock_m); exit(0); } $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit"; } # # カウンターファイルからカウンター値を読み出す # if (open(CNT, "$file_count")) { ($wh_cnt, $today_cnt, $yesterday_cnt) = split(/,/, ); close(CNT); } else { $wh_cnt = -1; } $today_cnt ||= 0; $yesterday_cnt ||= 0; if (! $cnt_mode and ($mode ne "sound")) { # # 環境変数TZを日本時間に設定する # $ENV{'TZ'} = "JST-9"; # # 日付を得る # ($sec, $min, $hour, $mday, $mon, $year) = localtime($tm); $date_now = sprintf("%04d/%02d/%02d", 1900 + $year, ++$mon, $mday); $time_now = sprintf("%02d:%02d:%02d", $hour, $min, $sec); ($tmp1, $tmp2, $tmp3, $last_mday, $last_mon) = localtime($tm - ($left_day*86400)), $last_mon++ if ($left_day); # # 日付ファイルから最終アクセス日付を読み出す # if (open(DATE, "$file_date")) { $date_log = ; close(DATE); } else { $date_log = ""; } # # カウンター値の整合チェック # if (($wh_cnt <= 0) and $date_log) { # # ログファイルからカウンター値を拾い出す # if (open(LOG, "$file_access")) { while ($i = ) { ($wh_cnt = $i) =~ tr/0-9//cd if ($i =~ /^COUNT/); } close(LOG); # # カウンタをカウンタファイルに書き戻す # if (open(CNT, "> $file_count")) { print(CNT $wh_cnt, ',', $today_cnt, ',', $yesterday_cnt); close(CNT); } } else { $wh_cnt = -1; } } # # 日付が異なる、つまり、今日初めてのアクセスであれば # if ($date_log and ($date_log ne $date_now)) { # # メールアドレスが設定してある時は、アクセスログをメールで送信する # if ($mail_to ne "") { $msg = "To: $mail_to\nFrom: $count_name\n"; $msg .= "Subject: ACCESS $date_log $today_cnt\n\n"; if ($account_detail and open(IN, "$file_access")) { $msg = join("", $msg,); close(IN); } elsif (! $account_detail and open(IN, "$file_access")) { $msg .= "TodayAccess = $today_cnt\n\n"; while ($i = ) { $msg = join("", $msg,$i) if (($i =~ /HIT!$/) .. ($i eq "\n")); } close(IN); } else { $msg .= "logfile not found\n"; } if (-x $sendmail) { open(MAIL, "| $sendmail $mail_to"); print(MAIL "$msg"); close(MAIL); } # # メールアドレスを設定していない時は、過去ログファイルを作成 # } else { ($tmp1, $log_mon, $log_mday) = split(/\//, $date_log); $log_mon = int($log_mon); $log_mday = int($log_mday); $file_acclog = "./$count_name/acl$log_mon-$log_mday.log"; if ($left_day and opendir(DIR, "$count_name")) { @logfile_name = grep(/^acl.+\.log$/, readdir(DIR)); closedir(DIR); foreach $i (@logfile_name) { ($log_mon, $log_mday) = ($i =~ /(\d+)-(\d+)/g); if ($last_mon > $log_mon) { push(@del_name, join('/', ".",$count_name,$i)) unless (($mon < $last_mon) and ($mon >= $log_mon)); } elsif (($last_mon == $log_mon) and ($last_mday > $log_mday)) { push(@del_name, join('/', ".",$count_name,$i)); } } foreach $i (@del_name) { if ($i =~ /^([-\/\w.]+)$/) { $i = $1; } else { die "Bad path in $i"; } } unlink(@del_name); } if ($file_acclog =~ /^([-\/\w.]+)$/) { $file_acclog = $1; } else { die "Bad path in $file_acclog"; } if (open(IN, "$file_access") and open(YLOG, "> $file_acclog")) { print(YLOG ); close(YLOG); close(IN); chmod(oct($pm), "$file_acclog"); } else { die "Logfile can't be made"; } } # # アクセスログを初期化する # close(LOG) if (open(LOG, "> $file_access")); # # 今日の日付を日付ログファイルに書き出す # print(DATE "$date_now"), close(DATE) if (open(DATE, "> $file_date")); # # 昨日のカウンター値を更新する # $yesterday_cnt = $today_cnt; $today_cnt = 0; } elsif (! $date_log) { print(DATE "$date_now"), close(DATE) if (open(DATE, "> $file_date")); } # # すでに同アドレスからのアクセスがあればカウントアップしない # $count_up = 1; if ($do_address_check and open(LOG, "$file_access")) { while ($i = ) { $count_up = ($i ne "ADDR = [ $ENV{'REMOTE_ADDR'} ]\n") or last; } close(LOG); } # # カウントアップ処理 # if (($wh_cnt >= 0) and $count_up) { # # カウンタをひとつインクリメントする # $wh_cnt++; $today_cnt++; # #カウンター値をイベント値と比較する # &compare_event; # # %7E や \~ などを処理する # $referer = ($ENV{'HTTP_REFERER'} or "none") if ($mode eq "text"); $ref_url =~ tr/\\//d; $ref_url =~ s|http%3A//|http://|; # # アクセスログを記録する # $addr = $ENV{'REMOTE_ADDR'}; if ($get_hostname) { $host = ($ENV{'REMOTE_HOST'} eq $addr) ? "" : $ENV{'REMOTE_HOST'}; $host ||= gethostbyaddr(pack('C4',split(/\./, $addr)),2) || $addr; } else { $host = $addr; } if (open(LOG, ">> $file_access")) { $count_msg = "COUNT = [ $wh_cnt ]"; $count_msg .= $eventkey ? "HIT!\n" : "\n"; print(LOG "$count_msg"); print(LOG "TIME = [ $time_now ]\n"); print(LOG "ADDR = [ $addr ]\n"); print(LOG "HOST = [ $host ]\n") if ($host ne $addr); print(LOG "AGENT = [ $ENV{'HTTP_USER_AGENT'} ]\n"); print(LOG "FROM = [ $ref_url ]\n") if ($ref_url and (! $my_url or ($ref_url !~ /$my_url/o))); print(LOG "REFER = [ $referer ]\n") if ($mode eq "text"); print(LOG "\n"); close(LOG); } if ($eventkey and open(ELOG, ">> $file_evtlog")) { print(ELOG "COUNT = [ $wh_cnt ]\n"); print(ELOG "DAY = [ $date_now ]\n"); print(ELOG "TIME = [ $time_now ]\n"); print(ELOG "ADDR = [ $addr ]\n"); print(ELOG "HOST = [ $host ]\n") if ($host ne $addr); print(ELOG "AGENT = [ $ENV{'HTTP_USER_AGENT'} ]\n"); print(ELOG "FROM = [ $ref_url ]\n") if ($ref_url and (! $my_url or ($ref_url !~ /$my_url/o))); print(ELOG "REFER = [ $referer ]\n") if ($mode eq "text"); print(ELOG "\n"); close(ELOG); } # # カウンタをカウンタファイルに書き戻す # print(CNT $wh_cnt, ',', $today_cnt, ',', $yesterday_cnt), close(CNT) if (open(CNT, "> $file_count")); } } # # 次のイベント値を求める # if (($cnt_mode eq "next") or ($mode eq "flash")) { if (open(EVT, "$file_event")) { while($i = ) { chomp($i); $next_event = ($wh_cnt < $i) ? $i : 0 and last; } close(EVT); } else { $next_event = 0; } unless (($next_event > 0) and ($next_event < 1000)) { $cnt_length = length($wh_cnt); if ($cnt_length < 4) { $next_event = 1000; } else { $regular_event1 = substr($wh_cnt, 0, 1); $regular_event2 = $regular_event1 x $cnt_length; $regular_event1++; $regular_event1 *= 10 ** ($cnt_length - 1); $next_event ||= $regular_event1; $next_event = $regular_event1 if (($wh_cnt < $regular_event1) and ($regular_event1 < $next_event)); $next_event = $regular_event2 if (($wh_cnt < $regular_event2) and ($regular_event2 < $next_event)); } } } # # イベント音楽の選択 # if ($mode eq "sound") { $count_up = 1; if ($do_address_check and open(LOG, "$file_access")) { while ($i = ) { ($cnt_tmp = $i) =~ tr/0-9//cd if ($i =~ /^COUNT/); $count_up = 0, last if (($i eq "ADDR = [ $ENV{'REMOTE_ADDR'} ]\n") and ($cnt_tmp != $wh_cnt)); } close(LOG); } &compare_event; $sound_locate = ($eventkey and $count_up) ? $event_sound : $normal_sound; } # # CGIスクリプトの結果としてカウンターを書き出す # $wh_cnt = 0 if ($wh_cnt == -1); { $cnt_mode eq "today" and $cnt_tmp = $today_cnt, last; $cnt_mode eq "yesterday" and $cnt_tmp = $yesterday_cnt, last; $cnt_mode eq "next" and $cnt_tmp = $next_event, last; $cnt_tmp = $wh_cnt; } $cntstr = (($figure > 0) and !$cnt_mode) ? sprintf(sprintf("%%0%dld", $figure), $cnt_tmp) : sprintf("%ld", $cnt_tmp); if ($mode eq "text") { if ($eventkey and $ssi_msg) { $ssi_msg =~ s/_cnt_/$cntstr/g; $cntstr = $ssi_msg; } print "Content-type: text/html\n\n$cntstr\n"; } elsif ($mode eq "gif") { print "Content-type: image/gif\n\n"; foreach $i (split(//, $cntstr)) { push(@files, ($cnt_mode and $img_dir) ? "./$img_dir/$i.gif" : "./big/$i.gif"); } if ($eventkey and ! $cnt_mode) { unshift(@files, "$topgif") if ($topgif); push(@files, "$lastgif") if ($lastgif); } require "./gifcat.pl"; binmode(STDOUT); print &gifcat::gifcat(@files); } elsif ($mode eq "hide") { print "Content-type: image/gif\n\n"; $size = -s $giffile; open(IMG, "./$giffile"); binmode(IMG); binmode(STDOUT); read(IMG, $buf, $size); print $buf; close(IMG); } elsif ($mode eq "flash") { print "Content-type: application/x-www-form-urlencoded\n\n"; print "count=$wh_cnt&today_count=$today_cnt&"; print "last_count=$yesterday_cnt&eventkey=$eventkey&nextevent=$next_event"; } elsif ($mode eq "sound") { print "Location: $sound_locate\n\n"; } # # ロック権を開放する # close(LOCK) if ($do_file_lock == 1); rmdir($file_lock_m) if ($do_file_lock == 2); # # イベント値比較ルーチン # sub compare_event { $eventkey = 0; if ($wh_cnt > 999) { $eventkey = (substr($wh_cnt, 1) == 0); unless ($eventkey) { $eventkey = 1; @cnts = split(//, $wh_cnt); foreach $i (@cnts) { $eventkey = ($cnts[0] == $i) or last; } } } if (! $eventkey and open(EVT, "$file_event")) { while($i = ) { chomp($i); $eventkey = ($wh_cnt == $i) and last; } close(EVT); } }