#!/usr/bin/perl

# +----------------------- 設定 ------------------------------- +
# 画像ディレクトリ
$imgdir = './cnt_img/count_';

# 同日中に同一IPがアクセスした場合のカウントアップ(0:する/1:しない)
$chkip = 1;

# 外部使用防止機能を(0:使う/1:使わない)
$chkurl = 1;

# 上記項目に「使う」を指定した場合カウンターを表示するURLを指定（複数指定可）
@URL = ('http://','http://');

# 時差調整（1時間遅らせる場合「-1*60*60」）
$lag = 0;

# ファイルロック(flock関数)を(0:使う/1:使わない)
$f_lock = 1;

# カウンターの桁数(フロー時はカウント値の桁数で表示)
$digit = 7;

# カウントログ格納用ディレクトリ
$cntdir = './data/';

# ロックファイル用ディレクトリ
$lockdir = './lock/';
# +--------------------- プログラム ------------------------ +

require './gifcon.pl';
$home = $ENV{'HTTP_REFERER'};
$name = $ENV{'QUERY_STRING'};
$addr = $ENV{'REMOTE_ADDR'};
$addr =~ s/\.//g;
$lockn = "$name\.tmp";
$tmpfile = "$lockdir$addr$lockn";

# カウンターファイル
$getcnt = "$cntdir$name"."cnt.dat";

$gettim = "$cntdir$name"."time.txt";
$nowtime = time + $lag;

if (!$chkurl) {
    if (!grep($home =~ /$_/i,@URL)) { &Error(1); }
}

&LockFile;

if ($chkip) { &Check; }
else { &CntUp; }


# +-------------- ロックファイルをチェック ----------------- +

sub LockFile {
	&LockList;
	if (grep(/$lockn/,$dirchk)) {
		for ($retry = 0; $retry <= 5; $retry++) {
			sleep(1);
			&LockList;
			if (!grep(/$lockn/,$dirchk)) { last; }
				if ($retry == 5) {
					&LockList;
					@TMP = split(/\s+/,$dirchk);
					foreach $tmp (@TMP) {
						$ltime = (stat("$lockdir$tmp"))[9] + $lag;
						$ntime = $nowtime - 30;
						if ($ltime < $ntime) { unlink "$lockdir$tmp" || &Error(4); }
					}
				&LockList;
				if (grep(/$lockn/,$dirchk)) { &Error(2); }
				else { last; }
			}
		}
	}
	open(FLAG, ">$tmpfile") || &Error(3);
	close(FLAG);
	chmod(0666, "$tmpfile");
}

sub LockList { $dirchk = `ls $lockdir$ls`; }


# +---------------- IPアドレスをチェック ------------------- +

sub Check {
	if (!-e $gettim) { &TimeLog; }
	$last = (localtime( (stat($gettim))[9] + $lag ))[3];
	$today = (localtime($nowtime))[3];
	if ($last != $today) {
		unlink "$gettim" || &Error(10);
		&TimeLog;
	}
	open(TLOG, "<$gettim") || &Error(11);
	$l = 0;
	while ($_ = <TLOG>) {
		chop;
		$IP[$l] = "$_";
		$l++;
	}
	close(TLOG);
	$iplist = grep /$addr/, @IP;
	if (!$iplist) {
		open(RTIP, ">>$gettim") || &Error(12);
		print RTIP "$addr\n" || &Error(12);
		close(RTIP);
		&CntUp;
	}
	else { &NoCnt; }
}

sub TimeLog {
	open(TIME, ">$gettim") || &Error(9);
	print TIME "$addr\n" || &Error(9);
	close(TIME);
	&CntUp;
}


# +------------------ カウント数値取得 --------------------- +
sub CntUp {
	&MakeCnt;
	open(WRITE, ">$tmpfile") || &Error(8);
	if (!$f_lock) { eval'flock(WRITE,2);'; }
	$count++;
	seek(WRITE, 0, 0);
	print WRITE "$count\n" || &Error(8);
	if (!$f_lock) { eval'flock(WRITE,8);'; }
	close(WRITE);
    rename("$tmpfile", "$getcnt") || &Error(5);
	&OutImage;
}

sub NoCnt {
	&MakeCnt;
	unlink "$tmpfile" || &Error(4);
	&OutImage;
}

sub MakeCnt {
	if (!-e $getcnt) {
		open(MAKE, ">$getcnt") || &Error(6);
		print MAKE "0\n" || &Error(6);
		close(MAKE);
		chmod(0666,"$getcnt");
	}
	open(READ, "<$getcnt") || &Error(7);
	$count = <READ>;
	close(READ);
	$count =~ s/\r\n//g;
	$count =~ s/\r//g;
	$count =~ s/\n//g;
}

# +--------------------- 画像出力処理 ---------------------- +

sub OutImage {
	if ($digit < length($count)) { $digit = $count; }
	else { $digit = sprintf("%0$digit\d",$count); }
	@DIGIT = split(//, $digit);
	$i = 0;
	foreach (@DIGIT) {
		$IMGF[$i] = "$imgdir$name$_.gif";
		$i ++;
	}

	binmode(STDOUT);
	print "Content-type: image/gif\n\n";
	print &gifcat'gifcat(@IMGF);
	exit;
}


# +------------------ エラー強制終了 ------------------- +

sub Error {
	print "Content-type: text/html\n\n";
	exit;
}

#error1(設定URLエラー)
#error2(ビジー状態)
#error3(ロックファイルオープンエラー)
#error4(ロックファイルクローズエラー)
#error5(ロックファイルリネームエラー)
#error6(カウントログ作成エラー)
#error7(カウント数値読込みエラー)
#error8(カウント数値書込みエラー)
#error9(タイムログ作成エラー)
#error10(タイムログ削除エラー)
#error11(タイムログ読込みエラー)
#error12(タイムログ書込みエラー)

