#!/usr/bin/perl
#↑perlまでのパスを指定（わからない場合はプロバイダに確認する）

#〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜#
#Copyright(C)2000 ohtan. All rights reserved.              #
#CmfCounter-TypeC ver2.1                                   #
#Build :2000/4/17                                          #
#Update:2001/9/8                                           #
#Home  :CounterMaterialFactory(CMF)                        #
#       http://cmf.ohtanz.com/                             #
#Mail  :cmf@ohtanz.com                                     #
#〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜#
#_______________________[ 設定項目 ]_______________________#

#[1]すべての桁をアニメーションさせるときは「0」
#   カウントアップした桁のみアニメーションさせるときは「1」を指定
$gifanm = 1;
#[2]不正使用防止機能を(0:使う/1:使わない)
$def = 1;
#[3]2を使う場合カウンターを表示するページのURLを指定(アクセスされると思われるURLを全て指定)
@URL = ('','');
#[4]ファイルロック(flock関数)を(0:使う/1:使わない)
$f_lock = 0;
#[5]GIF画像が格納されているディレクトリ(images)までのパス(通常は変更不要)
$imgdir = './images/';
#[6]カウントログ格納用ディレクトリまでのパス(通常は変更不要)
$cntdir = './data/';
#[7]ロックファイル監視用ディレクトリまでのパス(通常は変更不要)
$lockdir = './lock/';
#[8]同日中に同一IPがアクセスした場合のカウントアップ(0:する/1:しない)
$up = 0;
#[9]特定のホスト名、IPアドレスのカウント防止(使用しない時は空欄)
#   ホスト名を指定する時は[so-net.ne.jp][nifty.com]の部分を指定
#   IPアドレスを指定する時は先頭の2つ[210.189][203.35]の部分を指定
@DENY = ('','');
#[10]海外サーバー用時差調整(最初の数字を1〜24時間で指定)
#   (例:15時間遅らせる場合は[-15*60*60]進める場合は[15*60*60])
$jisa = 0;

#________[ 設定終了(以下の変更はperlの知識が必要) ]________

$home    = $ENV{'HTTP_REFERER'};
$name    = $ENV{'QUERY_STRING'};
$addr    = $ENV{'REMOTE_ADDR'};
$r_addr  = $addr;
$addr    =~ s/\.//g;
$nowtime = time + $jisa;
$tmpfile = "$lockdir$nowtime.tmp";
$gosign  = "$lockdir$addr.log";
$getcnt  = "$cntdir"."count.dat";
$safcnt  = "$cntdir"."safe.dat";
$iplog   = "$cntdir"."addr.dat";

if ($name =~ /=/) {
   ($keta,$value) = split(/=/,$name);
} else {
    $keta = $name;
}

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

&LockFile;

#__________________[ ロックファイル処理 ]__________________

sub LockFile {
    if ($value eq 'top') {
        &LockList;
        if (grep(/\.tmp/,@LKD)) {
            foreach (0..4) {
               sleep(1);
               if ($_ == 4) {
                   &LockList;
                   foreach (@LKD) {
                      $ltime = (stat("$lockdir$_"))[9] + $jisa;
                      $ntime = $nowtime - 30;
                      if ($ltime < $ntime) {
                          if (!unlink("$lockdir$_")) {
                              next;
                          }
                      }
                   }
                   &LockList;
                   if (grep(/\.tmp/,@LKD)) {
                       &Error(2);
                   } else {
                       last;
                   }
               }
            }
        }

        if (!open(FLAG,">$tmpfile")) {
            &Error(3);
        }
        close(FLAG);
        chmod(0666,$tmpfile);

        if (grep(/\.log/,@LKD)) {
            foreach (@LKD) {
               if ($_ =~ /\.log$/) {
                   if (!unlink("$lockdir$_")) {
                       next;
                   }
               }
            }
        }
        if ($DENY[0]) {
            &HostSch;
        }
        if ($up) {
            &IpCheck;
        } else {
            &CntUp;
        }
    } else {
        if ($DENY[0]) {
            &HostSch;
        }
        if (!-e $gosign) {
            foreach (0..4) {
               sleep(1);
               if ($_ == 4 || -e $gosign) {
                   last;
               }
            }
        }
        &NoCnt;
    }
}

sub LockList {
    opendir(READ,$lockdir);
    @LK = readdir(READ);
    closedir(READ);
    $l = 0;
    foreach (@LK) {
       if ($_ eq '.' || $_ eq '..') {
           next;
       }
       $LKD[$l] = $_;
       $l++;
    }
}

#_________________[ IPアドレスのチェック ]_________________

sub IpCheck {
    if (!-e $iplog) {
        &TimeLog;
    }
    $last  = (localtime((stat($iplog))[9] + $jisa))[3];
    $today = (localtime($nowtime))[3];
    if ($last != $today) {
        unlink($iplog);
        &TimeLog;
    }
    open(TLOG,"<$iplog");
    @IP = <TLOG>;
    close(TLOG);

    $l = 0;
    foreach (@IP) {
       $_ =~ s/\r|\n//g;
       if ($_ =~ /^$addr/) {
           &NoCnt;
       }
       elsif ($l == $#IP) {
           if (!open(RTIP,">>$iplog")) {
               &Error(4);
           }
           print RTIP "$addr\n";
           close(RTIP);
           &CntUp;
       }
       $l++;
    }
}

sub TimeLog {
    if (!open(TIME,">$iplog")) {
        &Error(5);
    }
    print TIME "$addr\n";
    close(TIME);
    chmod(0666,$iplog);
    &CntUp;
}

#____________________[ ホスト名の取得 ]____________________

sub HostSch {
    $r_host = $ENV{'REMOTE_HOST'};
    if ($r_host eq $r_addr || $r_host eq '') {
        $r_host = gethostbyaddr(pack("C4",split(/\./,$r_addr)),2);
        if ($r_host eq '') {
            $r_host = $r_addr;
        }
    }
    foreach (@DENY) {
       if ($r_host =~ /$_/) {
           $much = $_;
           last;
       }
       elsif ($r_addr =~ /$_/) {
           $much = $_;
           last;
       }
    }
    if ($much ne '') {
        &NoCnt;
    }
}

#___________________[ カウント数値取得 ]___________________

sub CntUp {
    &ReadCnt;

    $count++ if !$sign;

    &SafePrint;

    if (!open(WRITE,">$tmpfile")) {
        &Error(8);
    }
    if (!$f_lock) {
        eval'flock(WRITE,2);';
    }
    print WRITE "$count\n";
    if (!$f_lock) {
        eval'flock(WRITE,8);';
    }
    close(WRITE);

    rename($tmpfile,$getcnt);
    open(GO,">$gosign");
    close(GO);
    &OutImage;
}

sub NoCnt {
    &ReadCnt;
    if ($value eq 'top' && -e $tmpfile) {
        unlink($tmpfile);
    }
    &OutImage;
}

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

    &CntCheck;

    if (!$count || $count < $rdsaf) {
        $count = $rdsaf;
    }
}
sub CntCheck {
    $slast  = (localtime((stat($safcnt))[9] + $jisa))[3];
    $stoday = (localtime($nowtime))[3];
    if ($slast != $stoday) {
        unlink($safcnt);
    }
    if (!-e $safcnt) {
        if (!open(MKS,">>$safcnt")) {
            &Error(7);
        }
        print MKS "$count\n";
        close(MKS);
        chmod(0666,$safcnt);
    }
    open(RDS,"<$safcnt");
    @SAFE = <RDS>;
    close(RDS);
    $rdsaf = pop(@SAFE);
    $rdsaf =~ s/\r|\n//g;
}
sub SafePrint {
    if (!open(PRS,">>$safcnt")) {
        &Error(8);
    }
    print PRS "$count\n";
    close(PRS);
}

#_____________________[ 画像出力処理 ]_____________________

sub OutImage {
    @IMG = split(//,$count);
    @IMG = reverse(@IMG);
    $keta--;
    if ($gifanm) {
        &ImgCheck;
    }
    if ($IMG[$keta] eq '') {
        $IMG[$keta] = 0;
    }
    $image = $IMG[$keta];
    binmode(STDOUT);
    open(PNT,"<$imgdir$image.gif");
    print "Content-type: image/gif\n\n";
    print <PNT>;
    close(PNT);
    exit;
}
sub ImgCheck {
    if (!$keta) {
        $image = 'a'.$IMG[$keta];
    }
    elsif ($IMG[$keta] eq '') {
        $image = 0;
    }
    elsif (substr($count,-1,$keta) =~ /0{$keta}/) {
        $image = 'a'.$IMG[$keta];
    }
    else {
        $image = $IMG[$keta];
    }
    binmode(STDOUT);
    open(PNT,"<$imgdir$image.gif");
    print "Content-type: image/gif\n\n";
    print <PNT>;
    close(PNT);
    exit;
}

#__________________[ エラー画像出力処理 ]__________________

sub Error {
    $ergif= "error"."$_[0].gif";
    if (2 < $_[0] && -e $tmpfile) {
        unlink($tmpfile);
    }
    binmode(STDOUT);
    open(ERR,"<$imgdir$ergif");
    print "Content-type: image/gif\n\n";
    print <ERR>;
    close(ERR);
    exit;
}

#error1(設定URLエラー)
#error2(ビジー)
#error3(ロックファイルオープンエラー)
#error4(IPログの書込エラー)
#error5(IPログの作成エラー)
#error6(カウントログの作成エラー)
#error7(セーフログの作成エラー)
#error8(カウント数値の書込エラー)
#error9(カウント数値の読込エラー)
