#!/usr/local/bin/perl # ############################################################# ### ### CGIカウンター SDcountH Ver.2.02 ### (イメージタグ埋め込み型、クッキー対応、ログつき) ### ### (C) 2001 Sentora Dori ### http://lowpower.iis.u-tokyo.ac.jp/~hat ### ############################################################# # #################### 変数設定 #################### # カウンタファイル、ログファイルをおくディレクトリのパス $basedir = "."; # ログをとる (ブラウザに表示するときに指定する方が優先される) $takelog = 1; # 最も単純なログをとる(カウント数、アクセス時間、IPアドレスを順番に記録する) $takelog_pure = 1; # 日ごとのアクセス数をとる $takelog_day = 1; # ホストごとのアクセス数をとる $takelog_ip = 1; # クッキーの名前 (クッキー使用時) (通常は変更必要なし) $cookiename = 'SDcountH'; # クッキーの有効期間 (クッキー使用時) (必要ならば変更) $cookiedays = 365; # 2: flockを使用する 1: ロックファイルでロックする 0: ロックしない (通常は変更必要なし) $uselock = 2; #################### 変数設定(ここまで) #################### # 各ログデータの区切り記号 (変更必要なし) $delimiter ="---nextlog---\n"; # flockを使用しないロックのときのロックファイル名のベース (変更必要なし) $baselockfile = './SDlock'; ### data format ### require './gifcat.pl'; &init_form(); $countfile = "$basedir/$countname.txt"; $logfile = "$basedir/$countname.log"; ### get cookie #### if ($usecookie) { &init_cookie($cookiename); $yourcount = $cookie{$countname} + 1; } ### get host ### $hostaddr = $ENV{'REMOTE_ADDR'}; $hostname = $ENV{'REMOTE_HOST'}; if ($hostaddr eq $hostname || $hostname eq '') { $hostname = gethostbyaddr(pack("C4", split(/\./, $hostaddr)), 2); } ### data file open ### if (!open(OUT, "+<$countfile")) { &print_error('[no counter data]'); } # file lock if (!&lock_file(OUT)) { &print_error('[lock error]'); } # image file check if (!(-f "$gifdir/0.gif")){ &print_error("[no image files]"); } ### get counter ### seek(OUT, 0, 0); $totalcount = 1 + ; $yesterdaycount = 0 + ; $todaycount = 1 + ; $lastday = ; $lasthost = ; chomp($lasthost); ### reload or no count? ### if (($ban_reload || !$countup) && $hostaddr eq $lasthost) { $totalcount--; $todaycount--; &unlock_file(OUT); close(OUT); $nochange = 1; if ($usecookie) { $yourcount--; } goto SHOWPAGE; } ### get time ### ($sec, $min, $hour, $day, $mon, $year) = localtime(time); $mon++; $year += 1900; $today = "$year-$mon-$day\n"; ### first access today? ### if ($today ne $lastday) { ($sec2, $min2, $hour2, $day2, $mon2, $year2) = localtime(time - 24*60*60); $mon2++; $year2 += 1900; $yesterday ="$year2-$mon2-$day2\n"; if ($yesterday ne $lastday) { $yesterdaycount = 0; } else { $yesterdaycount = $todaycount - 1; } $todaycount =1; $lastday = $today; } ### put counter ### seek(OUT, 0, 0); print OUT "$totalcount\n"; print OUT "$yesterdaycount\n"; print OUT "$todaycount\n"; print OUT "$lastday"; print OUT "$hostaddr\n"; &unlock_file(OUT); close(OUT); ### take log ### SHOWPAGE: if ($takelog) { &take_log; } ### html header ### print "Content-type: image/gif\n"; if ($usecookie && !$nochange) { $cookie{$countname} = $yourcount; &print_cookie($cookiename, $cookiedays); } print "\n"; ### showcounter ### $count = $totalcount; $count = $yesterdaycount if ($type eq 'yesterday'); $count = $todaycount if ($type eq 'today'); $count = $yourcount if ($type eq 'your'); &show_counter($count); ### end ### exit(0); ################## ### subroutine ### ################## #----- ブラウザへ出力 ----- sub show_counter { local($count) = @_; @num = split(//, $count); if ($counter_order) { while (@num < $counter_order) { unshift(@num, "0"); } } for(0..$#num){ $num[$_]="$gifdir/$num[$_].gif"; } binmode(STDOUT); $| = 1; print &gifcat'gifcat(@num); } #----- ログファイル作成 ----- sub take_log { local($sec, $min, $hour, $day, $mon, $year); local($today, $lastday); local($logip, $logname, $logday, $logvalue); if (!open(LOGF, "+<$logfile")) { $msg = '[log file not found]'; return; } if (!&lock_file(LOGF)) { $msg = '[log file lock error]'; return; } @logf = ; $txtlist = join('', @logf); @loglist = split(/$delimiter/, $txtlist); ($sec, $min, $hour, $day, $mon, $year) = localtime(time); $mon++; $year += 1900; $today = "$year-$mon-$day"; if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } # ホストごとのログ if ($takelog_ip) { @eachlog = split(/\n/, $loglist[2]); for ($i = 0; $i< @eachlog; $i++) { ($logip, $logname, $logvalue) = split(/,/, $eachlog[$i]); if ($hostaddr eq $logip) { $logname = $hostname; $logvalue++; @bakline = ($logip, $logname, $logvalue); $eachlog[$i] = join(',', @bakline); $findip = 1; last; } } if (!$findip) { $logvalue = 1; @bakline = ($hostaddr, $hostname, $logvalue); $eachlog[@eachlog] = join(',', @bakline); } if (!$usecookie) { $yourcount = $logvalue; $yourcount-- if ($nochange); } $loglist[2] = join("\n", @eachlog)."\n"; } # ホストごとのアクセス数を取得するだけでカウントしないなら戻る if ($nochange) { &unlock_file(LOGF); close(LOG); return; } # 単純なログ if ($takelog_pure) { @eachlog = split(/\n/, $loglist[0]); $timelog ="$year-$mon-$day $hour:$min:$sec"; @bakline = ($totalcount, $timelog, $hostaddr, $hostname); $writeline = join(',', @bakline); unshift(@eachlog, $writeline); $loglist[0] = join("\n", @eachlog)."\n"; } # 日ごとのログ if ($takelog_day) { @eachlog = split(/\n/, $loglist[1]); ($logday, $logvalue) = split(/,/, $eachlog[0]); if ($logday eq $today) { $logvalue++; @bakline = ($logday, $logvalue); $eachlog[0] = join(',', @bakline); } else { @bakline =($today, "1"); $writeline = join(',', @bakline); unshift(@eachlog, $writeline); } $loglist[1] = join("\n", @eachlog)."\n"; } $txtlist = join("$delimiter", @loglist); seek(LOGF, 0, 0); print LOGF $txtlist; &unlock_file(LOGF); close(LOG); } #----- ブラウザへの入力データの取得 ----- sub init_form { local($query, @param, $method); $method = $ENV{'REQUEST_METHOD'}; $method =~ tr/A-Z/a-z/; if ($method eq 'post') { read(STDIN, $query, $ENV{'CONTENT_LENGTH'}); } else { $query = $ENV{'QUERY_STRING'}; } @param = split(/&/, $query); $countname = ($param[0] =~ /^\w+$/) ? $param[0] : ''; $countup = ($param[1] == 1) ? $param[1] : 0; $type = ($param[2] =~ /^\w+$/) ? $param[2] : 'total'; $counter_order = ($param[3] =~ /^\d+$/) ? $param[3] : 5; $gifdir = ($param[4]) ? $param[4] : './img/default'; $ban_reload = ($param[5] == 1) ? $param[5] : 0; $takelog = ($param[6] != '') ? $param[6] : $takelog; $usecookie = ($param[7] == 1) ? $param[7] : 0; } #----- クッキー情報の取得 ----- sub init_cookie { local($cookiename) = @_; local($name, $value, @pairs, $pair); @cookie_list = split(/;\s/, $ENV{'HTTP_COOKIE'}); foreach $cookie_list (@cookie_list) { ($cookie_name, $cookie_value) = split(/=/, $cookie_list); if ($cookie_name eq $cookiename) { $cookie_value =~ s/:/; /g; $cookie_value =~ s/_/=/g; @pairs = split(/;\s/, $cookie_value); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $name =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg; $value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg; $cookie{$name} = $value; } last; } } } #----- クッキー情報の出力 ----- sub print_cookie { local($cookiename, $days, $domain) = @_; local($cookiestr) = &make_cookie($cookiename); local($expiredate) = &get_expiredate_string($days); print "Set-Cookie: $cookiestr;"; print " expires=$expiredate;"; if ($domain) { print " domain=$domain;"; } print "\n"; } #----- クッキー情報の作成 ----- sub make_cookie { local($cookiename) = @_; local(@enccookie, @encstr); local($name, $value); local($encode) = '\%\+\;\,\=\&\_\:'; while (($name, $value) = each %cookie) { $name =~ s/([$encode])/'%'.unpack("H2", $1)/eg; $value =~ s/([$encode])/'%'.unpack("H2", $1)/eg; $name =~ s/\s/\+/g; $value =~ s/\s/\+/g; push(@enccookie, "${name}_${value}"); } $encstr = join(':', @enccookie); return "$cookiename=$encstr"; } #----- クッキー用の有効期限時刻文字列の取得 ----- sub get_expiredate_string { local($days) = @_; local(@month) = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); local(@week) = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); local($sec, $min, $hour, $day, $mon, $year, $weekday) = gmtime(time + $days * 24*60*60); local($expiredate); $year += 1900; if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } $weekstr = $week[$weekday] ; $monstr = $month[$mon]; return "$weekstr, $day-$monstr-$year $hour:$min:$sec GMT"; } #----- ファイルロック ----- sub lock_file { local($FILE) = @_; local($LOCK) = "LOCK$FILE"; local($lockfile) = "$baselockfile$FILE"; local($retry) = 10; if ($uselock == 2) { eval(flock($FILE, 2)); if ($@) { return 0; } else { return 1; } } elsif ($uselock == 1) { while (-f $lockfile) { sleep(0.3); return 0 if (--$retry <= 0); } open($LOCK, ">$lockfile") || return 0; close($LOCK); return 1; } else { return 1; } } #----- ファイルアンロック ----- sub unlock_file { local($FILE) = @_; local($lockfile) = "$baselockfile$FILE"; if ($uselock == 2) { flock($FILE, 8); } elsif ($uselock == 1) { unlink($lockfile); } } ##### エラー処理 ##### #----- エラー文を出力し終了 ----- sub print_error { local($msg) = @_; print "Content-type: text/plain\n\n"; print $msg; exit(0); }