#!/usr/bin/perl

;# ↑--- ◎Perl5のパスを指定。このパスはプロバイダに確認。
;#
;#   アクセス解析 [アクセス・トラッカー Pro] ver.1.21 (Share)
;#   Copyright(C)1997-1999 Iwao Wada. All rights reserved.
;#

#-----------------------------------------------------------------------------#
# ここから初期設定です（サーバーへの設置方法は同梱の'readme.txt'をご覧下さい）#
#-----------------------------------------------------------------------------#
#
#   ●印の項目は必ず変更してください。
#   ○印の項目はレイアウト、オプション機能関連です。基本的に変更不要です。
#   ◎印の項目はサーバー環境によって変更する必要があります。（1行目も含みます）
#
#  ・ ''内に記述しますが、'を入れたい場合は 中の'は \'にして下さい。
#  ・ 行の頭に"#"を付けると、その行は実行時に無視されます。(コメントアウト)
#  ・ 詳しくは本サイトの解説/FAQ等を参照してください。
#
#
#-------- 特定機能の使用に関する設定 ------------------------------------------
#
#  ◎印はサーバー環境によっては正常に動作しない場合があります。
#  その場合は、その機能を無効にしてください。

$jcode = 'sjis';		# ○ 文字変換コード（sjis,jis,euc）
$max = 3000;			# ○ ログの最大保持数（あまり大きくしない）
$dmax = 60;			# ○ 日付別ログの最大保持日数（あまり大きくしない）
$days = 60;			# ○ クッキーの有効日数（再訪問として扱う日数）
$hold = 10;			# ○ 重複記録防止のためのIPアドレス保持数
				#     * 0 にするとページビューで解析されます。

$plus = 0;			# ○ 累計値にプラスする数値 (カウンター使用時のみ)

$uvst = 0;			# ○ ユニークビジターのみを解析（1:する／0:しない）
				#     * 同一者は1日に1回しかカウントしない
				#       ($holdを有効にしている必要があります)

$lock = 2;			# ◎ ファイルロック（1:使う<open>／2:使う<symlink>／0:使わない）
$cchek = 1;			# ○ ブラウザクッキーサポート確認（1:する／0:しない）
				#     * 解析ページに確認用HTMLを記述する必要があります。

$cname = 'mesamis';		# ○ クッキーの名前


#-------- パス/アドレス/ファイル名に関する設定 --------------------------------
#
#  スクリプトと同じディレクトリは、'./'です。1つ上は、'../'です。ここでの
#  「パス」とはサーバー内での場所 '../xxxx'や '/home/foo/public_html/'を
#  「アドレス」とはインターネット全体から見た場所 'http://〜'を指します。

require './stdio.pl';		# ○ CGI標準入出力ライブラリ"stdio.pl"のパス
require './jcode.pl';		# ○ 日本語変換ライブラリ"jcode.pl"のパス

# ↓ ○GIF画像連結ライブラリ"gifcat.pl"のパス
#      アクセスカウンターを使う場合のみ必要です。使わない場合はコメントアウトする。
require './gifcat.pl';

$datadir = './data/';		# ○ ログファイル格納用のディレクトリのパス
$errname = 'errors.txt';	# ○ エラーファイルの名前
$image = 'clear.gif';		# ○ ダミー画像のパス（カウンターを使用時は不要）


# ↓ ○ あなたのサイトのトップディレクトリのアドレス（外部からの不正利用を防止する場合）
#       ファイル名では無いので注意。(例：'http://www.bar.ne.jp/~foo/')
$site = '';


#/*----------------------------------------------------------------------------------*
# *                                                                                  *
# *  ・本スクリプトで初期設定が必要なのはここまでです。                              *
# *  ・スクリプトを改造する場合は、perlやCGIなどのそれなりの知識が必要です。         *
# *                                                                                  *
# *----------------------------------------------------------------------------------*/


#/*------------------- 基本情報の設定/関数のコール ----------------------------------*/

$SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit";


%FORM   = stdio::QueryString(1,2);

#/*---	エラーチェック		---*/
if ($ENV{'HTTP_REFERER'} !~ /$site/ && $site) { Errors(); }
elsif (!$FORM{'file'}) { Errors('システムエラー','ファイル名が指定されていません。'); }

#/*---	ファイル名の設定	---*/
$datafile = $datadir . $FORM{'file'} . ".acs";
$addrfile = $datadir . $FORM{'file'} . '.adr';

$cook = stdio::SetTime('ww2, dd-mm2-yyyy 23:59:59 GMT') if ($uvst);
$date = stdio::SetTime('yyyy/mm/dd ww hh:nn');
$now  = stdio::SetTime('yyyy/mm/dd (ww) hh:nn:ss');

#/*---	比較用日付の調整	---*/
($tday,$week,$time) = split(/ /, $date);
($time) = split(/:/, $time);
($year,$month) = split(/\//, $tday);
$month = $year . '/' . $month;


#/*---	環境変数の設定		---*/
if ($ENV{'REMOTE_HOST'} eq $ENV{'REMOTE_ADDR'} || !$ENV{'REMOTE_HOST'}) {
	$ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'} if (!$ENV{'REMOTE_HOST'});
	$ENV{'REMOTE_HOST'} = gethostbyaddr(pack('C4',split(/\./,$ENV{'REMOTE_HOST'})),2) || $ENV{'REMOTE_ADDR'};
}
$host = $ENV{'REMOTE_HOST'};

#/*---	リモートホスト名の設定	---*/
if ($host =~ /^(\d*).(\d*).(\d*).\d*$/)  { $host = "$1.$2.$3.*"; }
elsif ($host =~ /.*\.(.*)\.(.*)\.(.*)$/) { $host = "*.$1.$2.$3"; }
elsif ($host =~ /.*\.(.*)\.(.*)$/)	 { $host = "*.$1.$2";    }

#/*---	IPアドレスの記録	---*/
if ($hold) {
	@ADDR = stdio::ReadFile($addrfile);
	if ($ADDR[0]) {
		foreach (@ADDR) {
			$ip = $_;
			chomp($ip);
			if ($ip eq $ENV{'REMOTE_ADDR'}) {
				$no_count = 1;
				last;
			}
		}
	} else {
		shift(@ADDR);
	}
	if (!$no_count) {
		unshift(@ADDR, "$ENV{'REMOTE_ADDR'}\n");
		splice(@ADDR, $hold) if ($#ADDR >= $hold);
		if (open(FILE,">$addrfile")) {
			print FILE @ADDR;
			close(FILE);
		}
	}
}

$FORM{'img'} = $image if ($FORM{'img'} !~ /\.gif$/i && !$FORM{'type'});

if (!$FORM{'jump'}) {
	
	$link = $FORM{'link'};
	$link =~ s/(.*)\?.*/$1?/i;
	
	#/*---	サーチエンジンの判別	---*/
	if ($link eq 'http://search.yahoo.co.jp/bin/search?' || $link eq 'http://search.yahoo.com/bin/search?') {
		$sname = 'p';
	} elsif ($link eq 'http://www.infoseek.co.jp/Titles?' || $link eq 'http://japan.infoseek.com/Titles?') {
		$sname = 'qt';
	} elsif ($link eq 'http://www.goo.ne.jp/default.asp?') {
		$sname = 'MT';
	} elsif ($link eq 'http://www.excite.co.jp/search.gw?' || $link eq 'http://jp.excite.com/search.gw?') {
		$sname = 's';
	} elsif ($link eq 'http://www.lycos.co.jp/cgi-bin/pursuit?') {
		$sname = 'query';
	} elsif ($link eq 'http://search.netplaza.biglobe.ne.jp/cgi-bin/search-renew3.cgi?') {
		$sname = 'key';
	} elsif ($link eq 'http://search.fresheye.com/?') {
		$sname = 'kw';
	} elsif ($link eq 'http://para.cab.infoweb.ne.jp/cgi-bin/para?') {
		$sname = 'Querystring';
	} elsif ($link eq 'http://kaze.csj.co.jp/search/lookup-main.cgi?') {
		$sname = 'key';
	} elsif ($link eq 'http://www.altavista.com/cgi-bin/query?') {
		$sname = 'q';
	} else {
		$cant_get_key = 1;
	}
	
	#/*---	キーワードの取得	---*/
	if (!$cant_get_key) {
		
		($trash,$key) = split(/\?/, $ENV{'QUERY_STRING'});
		@KEY = split(/&/, $key);
		foreach (@KEY) {
			($name,$value) = split(/=/, $_);
			if ($name eq $sname) {
				$keyword = $value;
				last;
			}
		}
		
		#/*---	キーワードデコード	---*/
		if ($keyword) {
			$keyword =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
			$keyword =~ tr/+/ /;
			$keyword =~ s/&/&amp;/g;
			$keyword =~ s/"/&quot;/g;
			$keyword =~ s/</&lt;/g;
			$keyword =~ s/>/&gt;/g;
			jcode::convert(*keyword,"$jcode") if ($jcode);
		}
	}

} else {
	$link = $FORM{'link'};
}

#/*---	個別クッキー１ (前回訪問日時記録用)	---*/
%COOKIE1 = stdio::GetCookie("$cname.1.$FORM{'file'}");

#/*---	個別クッキー２ (重複記録防止用)		---*/
%COOKIE2 = stdio::GetCookie("$cname.2.$FORM{'file'}");

#/*---	共通クッキー１ (移動状況把握用)		---*/
%COOKIE3 = stdio::GetCookie($cname);

#/*---	共通クッキー２ (クッキーサポート確認用)	---*/
%COOKIE4 = stdio::GetCookie('CookieStatus');

$repeat = $check = 0;

$COOKIE4{'cookie_status'} = 1 if(!$cchek);


#/*---	クッキーサポートのチェック	---*/
$check = -1 if (!$COOKIE4{'cookie_status'} && $cchek);

$COOKIE2{'NoCount'} = 1 if ($FORM{'type'} eq 'today' || $FORM{'type'} eq 'ysday');

if (!$COOKIE2{'NoCount'}) {
	
	#/*---	閲覧ページ数の設定	---*/
	$pages = $COOKIE3{'pages'} + 1 if ($COOKIE3{'pages'});
	
	#/*---	前回訪問日の取得	---*/
	$check = $COOKIE1{'repeat'} if ($COOKIE1{'repeat'});
	if ($COOKIE1{'revisit'}) { $COOKIE1{'revisit'} ++; }
	else { $COOKIE1{'revisit'} = 1; }
	
} else {
	$pages = $COOKIE3{'pages'};
	$no_count = 1;
}

#/*---	セッション内はカウントしない	---*/
if (!$no_count) {
	
	#/*---	ロックのチェック	---*/
	if (!stdio::LockCheck($lockfile,$lock)) { exit(1); }
	
	#/*---	ファイルリード		---*/
	@FILE = stdio::ReadFile($datafile);
	
	($urls) = split(/\?/, $ENV{'HTTP_REFERER'});
	
	#/*---	ファイルが空の場合	---*/
	if (!$FILE[1]) {
		$FILE[0] = "0\t0\t0\t0\t$tday\t$urls\t$now";
		$FILE[2] = "日:0|0|0:0|0|0\t月:0|0|0:0|0|0\t火:0|0|0:0|0|0\t水:0|0|0:0|0|0\t木:0|0|0:0|0|0\t金:0|0|0:0|0|0\t土:0|0|0:0|0|0\t$tday";
		$FILE[3] = "0:0|0|0:0|0|0\t1:0|0|0:0|0|0\t2:0|0|0:0|0|0\t3:0|0|0:0|0|0\t4:0|0|0:0|0|0\t5:0|0|0:0|0|0\t"
			 . "6:0|0|0:0|0|0\t7:0|0|0:0|0|0\t8:0|0|0:0|0|0\t9:0|0|0:0|0|0\t10:0|0|0:0|0|0\t11:0|0|0:0|0|0\t"
			 . "12:0|0|0:0|0|0\t13:0|0|0:0|0|0\t14:0|0|0:0|0|0\t15:0|0|0:0|0|0\t16:0|0|0:0|0|0\t17:0|0|0:0|0|0\t"
			 . "18:0|0|0:0|0|0\t19:0|0|0:0|0|0\t20:0|0|0:0|0|0\t21:0|0|0:0|0|0\t22:0|0|0:0|0|0\t23:0|0|0:0|0|0";
		$FILE[4] = "$month:0:0";
		$first = 1;
	} else {
		chomp($FILE[0]);
		chomp($FILE[1]);
		chomp($FILE[2]);
		chomp($FILE[3]);
		chomp($FILE[4]);
	}
	
	#/*---	総合ログの集計	---*/
	($count,$tocnt,$yscnt,$rept,$today,$url,$now) = split("\t", $FILE[0]);
	$count ++;
	if ($today ne $tday) {
		$yscnt = $tocnt;
		$tocnt = 1;
	} else {
		$tocnt ++;
	}
	
	$url = $urls if ($urls);
	
	$rept ++ if ($check && $check != -1);
	
	$data = "$count\t$tocnt\t$yscnt\t$rept\t$tday\t$url\t$now\n";
	
	#/*---	日付別ログの集計	---*/
	$i = $find = 0;
	@DAILY_LOG = split("\t", $FILE[1]);
	if ($tocnt != 1 && $FILE[1]) {
		foreach (@DAILY_LOG) {
			($type,$visit,$repeat) = split(/:/, $_);
			if ($tday eq $type) {
				$visit ++;
				$repeat++ if ($check && $check != -1);
				$find = 1;
				last;
			}
			$i ++;
		}
		$new_log = "$type:$visit:$repeat";
		splice(@DAILY_LOG, $i, 1, $new_log);
	} else {
		$repeat = 1 if ($check && $check != -1);
		$new_log = "$tday:1:$repeat";
		unshift(@DAILY_LOG, $new_log);
		splice(@DAILYL_LOG, $dmax) if ($#DAILY_LOG > $dmax);
	}
	$daily_log = join("\t", @DAILY_LOG);
	$daily_log .= "\n" if ($daily_log !~ /\n$/);
	
	#/*---	週別ログの集計		---*/
	$i = $find = 0;
	@WEEKLY_LOG = split("\t", $FILE[2]);
	
	$fin = pop(@WEEKLY_LOG);
	foreach (@WEEKLY_LOG) {
		
		last if (!$_);
		($type,$visit,$repeat) = split(/:/, $_);
		
		if ($week eq '日' && $fin ne $tday) {
			
			($visit1,$visit2,$visit3) = split(/\|/, $visit);
			($repeat1,$repeat2,$repeat3) = split(/\|/, $repeat);
			
			$visit3  = $visit2;
			$repeat3 = $repeat2;
			$visit2  = $repeat2 = 0;
			
			if ($week eq $type) {
				$visit1 ++;
				$visit2 ++;
				$repeat1 ++ if ($check && $check != -1);
				$repeat2 ++ if ($check && $check != -1);
			}
			
			$visit  = "$visit1|$visit2|$visit3";
			$repeat = "$repeat1|$repeat2|$repeat3";
			
			$WEEKLY_LOG2[$i] = "$type:$visit:$repeat";
			
		} elsif ($week eq $type) {
			
			($visit1,$visit2,$visit3) = split(/\|/, $visit);
			($repeat1,$repeat2,$repeat3) = split(/\|/, $repeat);
			
			$visit1 ++;
			$repeat1 ++ if ($check && $check != -1);
			
			$visit2 ++;
			$repeat2 ++ if ($check && $check != -1);
			
			$visit  = "$visit1|$visit2|$visit3";
			$repeat = "$repeat1|$repeat2|$repeat3";
			
			$find = 1;
			push(@WEEKLY_LOG, $tday);
			last;
		}
		$i ++;
	}
	
	if ($find) {
		$new_log = "$type:$visit:$repeat";
		splice(@WEEKLY_LOG, $i, 1, $new_log);
	} elsif (@WEEKLY_LOG2) {
		push(@WEEKLY_LOG2, $tday);
		@WEEKLY_LOG = @WEEKLY_LOG2;
	} else {
		Errors('ログエラー','曜日別アクセスのログが破損しています。');
	}
	
	$weekly_log = join("\t", @WEEKLY_LOG);
	$weekly_log .= "\n" if ($weekly_log !~ /\n$/);
	
	#/*---	時間別ログの集計	---*/
	$i = $find = 0;
	@HOUR_LOG = split("\t", $FILE[3]);
	
	if ($tocnt == 1 && !$first) {
		
		foreach (@HOUR_LOG) {
			
			($type,$visit,$repeat) = split(/:/, $_);
			
			($visit1,$visit2,$visit3) = split(/\|/, $visit);
			($repeat1,$repeat2,$repeat3) = split(/\|/, $repeat);
			
			$visit3 = $visit2;
			$repeat3 = $repeat2;
			
			$repeat2 = $visit2 = 0;
			
			$visit  = "$visit1|$visit2|$visit3";
			$repeat = "$repeat1|$repeat2|$repeat3";
			
			$NEW_HOUR_LOG[$i] = "$type:$visit:$repeat";
			
			$i ++;
		}
		@HOUR_LOG = @NEW_HOUR_LOG;
	}
	
	$i = 0;
	foreach (@HOUR_LOG) {
		
		($type,$visit,$repeat) = split(/:/, $_);
		
		($visit1,$visit2,$visit3) = split(/\|/, $visit);
		($repeat1,$repeat2,$repeat3) = split(/\|/, $repeat);
		
		if ($time == $type) {
			
			$visit1 ++;
			$repeat1 ++ if ($check && $check != -1);
			
			$visit2 ++;
			$repeat2 ++ if ($check && $check != -1);
			$visit  = "$visit1|$visit2|$visit3";
			$repeat = "$repeat1|$repeat2|$repeat3";
			$find = 1;
			last;
		}
		$i ++;
	}	
	
	if (!$find) { Errors('ログエラー','時間帯別アクセスのログが破損しています。'); }
	
	$new_log = "$type:$visit:$repeat";
	splice(@HOUR_LOG, $i, 1, $new_log);
	
	$hour_log = join("\t", @HOUR_LOG);
	$hour_log .= "\n" if ($hour_log !~ /\n$/);
	
	
	#/*---	月間別ログの集計	---*/
	$i = $find = 0;
	@MONTHLY_LOG = split("\t", $FILE[4]);
	($type,$visit,$repeat) = split(/:/, $MONTHLY_LOG[$#MONTHLY_LOG]);
	if ($type eq $month) {
		$visit ++;
		$repeat++ if ($check && $check != -1);
		$MONTHLY_LOG[$#MONTHLY_LOG] = "$month:$visit:$repeat";
	} else {
		$new_log = "$month:$visit:$repeat";
		push(@MONTHLY_LOG, $new_log);
	}
	$monthly_log = join("\t", @MONTHLY_LOG);
	$monthly_log .= "\n" if ($monthly !~ /\n$/);
	
	#/*---	個別ログの集計	---*/
	$logs = "$date\t$host\t$link\t$ENV{'HTTP_USER_AGENT'}\t$keyword\t$check\t$COOKIE1{'revisit'}\t$pages\t$FORM{'screen'}\t$FORM{'color'}\n";
	
	#/*---	配列をシフト	---*/
	for (1 .. 5) { shift(@FILE); }
	
	#/*---	古いログを削除	---*/
	if ($max < $#FILE) {
		splice(@FILE, $max);
	}
	
	#/*---	レコードの統括	---*/
	unshift(@FILE, $logs);
	unshift(@FILE, $monthly_log);
	unshift(@FILE, $hour_log);
	unshift(@FILE, $weekly_log);
	unshift(@FILE, $daily_log);
	unshift(@FILE, $data);
	
	#/*---	ロックのチェック	---*/
	if (!stdio::LockCheck($datafile,$lock)) { Error(); }

	#/*---	ファイルのロック/入出力	---*/
	$locked = stdio::FileLock($datafile,$lock,@FILE);

	if ($locked == -1) { Errors('システムエラー','このサーバーではシンボリックリンクが使えません。'); }
	elsif (!$locked)   { Errors(); }

#/*---	カウンターを表示する	---*/
} elsif ($FORM{'type'}) {
	
	#/*---	ファイルリード		---*/
	if (open(FILE,$datafile)) {
		$count_data = <FILE>;
		close(FILE);
	}
	($count,$tocnt,$yscnt) = split("\t", $count_data);
	
}

$pages = 1 if (!$pages);
$date =~ s/(\W)/sprintf("%%%02X", unpack("C", $1))/eg;


print "Location: $FORM{'link'}\n" if ($FORM{'jump'});

#/*---	ヘッダータイプ出力	---*/
print "Content-type: image/gif\n";

#/*---	クッキー処理		---*/
stdio::SetCookie("$cname.1.$FORM{'file'}","repeat:$date&revisit:$COOKIE1{'revisit'}",$days);
stdio::SetCookie("$cname.2.$FORM{'file'}","NoCount:1",$cook) if($hold);
stdio::SetCookie($cname,"pages:$pages","");

print "\n";

exit(0) if ($FORM{'jump'});

#/*---	カウンター処理		---*/
if ($FORM{'type'}) {
	if ($FORM{'type'} eq 'today')	 { $count = $tocnt; }
	elsif ($FORM{'type'} eq 'ysday') { $count = $yscnt; }
	elsif ($FORM{'type'} eq 'revst') { $count = $COOKIE1{'revisit'}; }
	else				 { $count += $plus; }
	PrintCounter($count,$FORM{'fig'},$FORM{'img'}) if (-e $FORM{'img'});
}

#/*---	ダミー画像を返す	---*/
stdio::FileOutput($FORM{'img'});
exit(0);

#/*------------------- カウンターを表示する -----------------------------------------*/

sub PrintCounter {
	
	local($count,$fig,$imgdir) = @_;
	$i = 0;
	$count = 0 if (!$count || $count !~ /\d/);
	$count = sprintf("%0$fig\d",$count) if ($fig > 2 && $fig < 12);
	
	foreach (split(//, $count)) {
		$IMG[$i] = $imgdir . '/' . $_ . '.gif';
		$i ++;
	}
	
	binmode(STDOUT);
	print gifcat::gifcat(@IMG);
	exit(0);
}

#/*------------------- エラー発生時の処理 -------------------------------------------*/

sub Errors {
	
	$errfile = $datadir . $errname;
	if ($_[0]) {
		
		if (open(FILE,">>$errfile")) {
			print FILE "$now,$_[0],$_[1]\n";
			close(FILE);
		}
	}
	
	if ($_[0] ne 'ログエラー') {
		
		print "Content-type: image/gif\n\n";
		stdio::FileOutput($FORM{'img'});
		exit(1);
		
	}
}

sub sigexit {
	if (-e $stdio::lockfile) { unlink($stdio::lockfile); }
	if (-e $stdio::tempfile) { unlink($stdio::tempfile); }
	exit(0);
}
