#!/usr/bin/perl

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
#
# 「便利カウンタ １．３１」
#
# Copyright (C) Atsushi Nakahira All Rights Reserved.
#
# 著作権は、作者である Atsushi Nakahira が持っています。
# 改造は自由に行ってもらって結構です。
# また、このカウンタを使用して発生したいかなる損害に対しても
# 作者は一切責任を負わないものとします。
# 自己管理の元このカウンタを運営して下さい。
# ---------------------------------------------------------------------
# １行目の #! の右側には、perl（インタプリタ）へのパスを入れて下さい。
# この事については、プロバイダーのホームページなどを参照して下さい。
# （１行目の #! 以外の # の右側に書かれている文章は、全てコメントです。
# プログラム的には全く関係ありません。）
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

# ☆☆☆　初期設定　☆☆☆

# カウント方式が、ページ表示回数（ページビュー）の場合は 0
# 連続同一IPアドレスのアクセスをカウントしない場合は 1 として下さい。
# 1 の方が、訪問者数に近い数値になりますが、かなりアクセス数が多い場合は、
# ほとんどページビューと同じ数値になってしまいます。

$houhou = 1;

###########################################################################################################
# ここから下の設定は、同じフォルダ内に２つ以上のカウンタを設置しない時には、通常変更する必要はありません。#
# しかし、ファイル名を変更した方がセキュリティー上ではより安全となります。                                #
###########################################################################################################

# 出力画像ファイル名
$imagefile = './toumei.gif';

# アクセスログ（今日のアクセス数）保存ファイル名（＆設置場所）
$acfile = './counter.log';

# アクセスログ（１日毎の）保存ファイル名（＆設置場所）
$dayfile = './day.log';

# 環境設定用ファイル名（＆設置場所）
$setfile = './settei.log';

# ロックファイル名（＆設置場所）
$lockfile = './lock.lock';

# jcode.plの設置場所
require './jcode.pl';

# ☆☆☆　初期設定終了　☆☆☆

# ☆☆☆　メイン処理　☆☆☆
@buhin = split(/&/, $ENV{'QUERY_STRING'});
foreach (@buhin) {
	($name, $value) = split(/=/, $_);
	$value =~ tr/+/ /;
	$value =~ s/%([A-Fa-f0-9]{2})/pack("C", hex($1))/ge;
	&jcode'convert(*value,'sjis');
	$value =~ s/</&lt;/g;
	$value =~ s/>/&gt;/g;
	$value =~ s/,/、/g;
	$form{$name} = $value;
}

$name = $form{'name'};

&kousin;

&gazou;

# ☆☆☆　メイン処理終了　☆☆☆

# ☆☆☆　ここからサブルーチン　☆☆☆

sub gazou {
	print "Content-type: image/gif\n\n";
	open(IMG, "$imagefile") or die("$imagefile のオープンに失敗しました。\n");
	binmode IMG;
	binmode STDOUT;
	while (<IMG>) {
		print STDOUT $_;
	}
	close IMG;

	exit;
}

sub kousin {
	$addr = $ENV{'REMOTE_ADDR'};
	$page = $ENV{'HTTP_REFERER'};

	# 日本の日時を取得
	&japan_time;

	$year = $year + 1900;
	$month = $mon + 1;
	@youbi = ('日','月','火','水','木','金','土');
	$youbi = $youbi[$wday];
	$date = sprintf("%d年%02d月%02d日（%s）", $year, $month, $mday, $youbi);

	# この値は大事なので値取得ミス回避用
	&gazou unless (length($date) == 20);

	&lock;

	open(IN, "$acfile") or &error("$acfile のオープンに失敗しました。");
	@naiyou = <IN>;
	close(IN);

	chomp @naiyou;

	$flag = 1;
	undef @kousin;

	&sansyou if (@naiyou);

	# アクセス数０もしくは新ページ名の処理
	if ($flag) {
		@jikan = ("0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0");
		$jikan[$hour] = 1;
		$jikan = join(",", @jikan);
		$hitotu = "$name\,1\,$page\,$addr\,$jikan\n";
		push(@kousin, $hitotu);
	}

	# 最終行に年月日を追加
	push(@kousin, $date);

	open(OUT, ">$acfile") or &error("$acfile の生成に失敗しました。");
	print OUT @kousin;
	close(OUT);

	&lock_kaijyo;
}

# ページ名参照処理
sub sansyou {
	# １つ前のアクセス者の日にちの削除＆取得
	$counter_day = pop @naiyou;

	if ($counter_day eq $date) {
		foreach $hitotu(@naiyou) {
			($name2,$suuji2,$page2,$addr2,@jikan) = split(/,/, $hitotu);

			if ($name eq $name2) {
				$flag = 0;
				if (($addr ne $addr2) or ($houhou == 0)) {
					$suuji2++;
					$jikan[$hour]++;
					$jikan = join(',', @jikan);
					$page2 = $page if ($page);
					$hitotu = "$name\,$suuji2\,$page2\,$addr\,$jikan";
				}
			}
			# chompで削除した改行付け足し
			$hitotu = "$hitotu\n";
			push(@kousin, $hitotu);
		}
	} else {
		# 環境設定を読み込み
		open(IN, "$setfile") or &error("$setfile のオープンに失敗しました。");
		$kankyou = <IN>;
		close(IN);

		chomp $kankyou;

		($password,$sentaku,$hozon,$hyouji) = split(/<>/, $kankyou);

		# 日毎ログ読み込み
		open(IN, "$dayfile") or &error("$dayfile のオープンに失敗しました。");
		@naiyou2 = <IN>;
		close(IN);

		undef @tuika;
		foreach $hitotu(@naiyou) {
			($name2,$suuji2,$page2,$addr2,@jikan) = split(/,/, $hitotu);
			$name2 = "<>$name2";
			push(@tuika, $name2, $suuji2, $page2, @jikan);
		}

		$tuika = join(',', @tuika);

		# 年月日を先頭に追加
		$tuika = "$counter_day$tuika\n";

		unshift(@naiyou2, $tuika);

		# 最大保存日数以上古い日削除
		if ($#naiyou2 >= $hozon) {
			splice(@naiyou2, $hozon);
		}

		# 日毎ログへ書き込み
		open(OUT, ">$dayfile") or &error("$dayfile の生成に失敗しました。");
		print OUT @naiyou2;
		close(OUT);
	}
}

sub lock {
	$wait = 8;
	while (-e $lockfile) {
		if ($wait > 0) {
			sleep(1);
			$wait--;
		} else {
			&error("混雑しています。");
		}
	}

	open(LOCK, ">$lockfile") or &error("$lockfile の生成に失敗しました。");
	close(LOCK);
}

sub lock_kaijyo {
	unlink($lockfile) if (-e $lockfile);
}

sub error {
	&lock_kaijyo;
	die $_[0];
}

# 日本時間に直して日時を取得
sub japan_time {
	$ENV{'TZ'} = "JST-9";
	($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
}

# ☆☆☆　サブルーチン終了　☆☆☆
