package session;
######################################################################

# セッション管理ライブラリ Ver1.0.2

# Copyright (c) 2003 Toshinori Kondo <kondo@bsp-net.com>
# $Id: session.pl,v 1.4 2003/07/05 16:16:51 toshi Exp $

######################################################################

# 更新履歴
#  Ver1.0.0 2003/07/01 初公開。モジュールが利用できない環境の為に。。
#  Ver1.0.1 2003/07/04 期限切れの全てのセッションを削除する関数を追加。
#  Ver1.0.2 2003/07/06 limit,clear関数のバグを修正。

# セッションのデータディレクトリは、
# 予め、環境変数「SessionTempDir」に格納して使用する事。

# セッションの有効期限は、
# 現在の時間から何秒先の未来まで有効かの秒数を
# 予め、環境変数「SessionLimit」に格納して使用する事。

# new関数
#   セッションIDの作成と、データファイルを生成します。
#   session::new();
#   成功時には、セッションIDを返します。
#   失敗時には、undefを返します。

# get関数
#   セッションから情報を引き出します。
#   session::get($SessionID, \%SessionData);
#   成功時には、1を返します。
#   失敗時には、undefを返します。

# set関数
#   セッションに情報を追加更新します。
#   session::set($SessionID, \%SessionData);
#   成功時には、1を返します。
#   失敗時には、undefを返します。

# delay関数
#   セッションの有効期限を延長します。
#   session::delay($SessionID);
#   成功時には、1を返します。
#   失敗時には、undefを返します。

# delete関数
#   セッションに関連付けられたデータを削除します。
#   session::delete($SessionID);
#   成功時には、1を返します。
#   失敗時には、undefを返します。

# clear関数
#   有効期限切れのセッションを全て削除します。
#   session::clear();
#   全てのセッションに対して、limit関数を呼び出します。
#   成功時には、1を返します。
#   失敗時には、undefを返します。

# generate関数
#   $ResultValue = session::generate();
#   ランダムな32桁の16進数値を生成します。
#   new関数が自動で呼び出すので、特に使用する必要は無い。

# limit関数
#   セッションが有効期限を確認し、期限切れなら削除する。
#   session::limit($SessionID);
#   get,set,delay関数で呼び出されますので、
#   明示的に確認する必要が無い限り、特に使用する必要は無い。
#   成功時には、1を返します。
#   失敗時には、undefを返します。

######################################################################

sub new {
	my ($SessionID, $SessionFile, $SessionLimit);
	if (-e $ENV{'SessionTempDir'}) {
		$SessionID = &generate();
		$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
		if ($ENV{'SessionLimit'}) {
			$SessionLimit = time() + $ENV{'SessionLimit'};
			if (!(-e $SessionFile)) {
				open SSID, "> $SessionFile";
				print SSID 'LIMITTIME=' . $SessionLimit . "\n";
				close SSID;
				if (-e $SessionFile) {
					chmod 0666, $SessionFile;
					return $SessionID;
				}
			}
		}
	}
	return undef;
}

sub get {
	my ($SessionID, $ref_session) = @_;
	my ($SessionFile, $temp, $SessionName, $SessionValue, $key);
	$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
	%$ref_session = ();
	&limit($SessionID);
	if (-e $SessionFile) {
		open SSID, $SessionFile;
		flock(SSID, 2);
		$temp = <SSID>;
		while (<SSID>) {
			$_ =~ s/\x0D?\x0A?$//;
			($SessionName, $SessionValue) = split(/=/, $_, 2);
			$ref_session->{$SessionName} = $SessionValue;
		}
		close SSID;
		$ref_session->{'SESSIONID'} = $SessionID;
		return 1;
	}
	return undef;
}

sub set {
	my ($SessionID, $ref_session) = @_;
	my (%TempSession, $SessionFile, $SessionLimit, $key);
	&limit($SessionID);
	$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
	if (-e $SessionFile) {
		open SSID, "+< $SessionFile";
		flock(SSID, 2);
		truncate(SSID, 0);
		seek(SSID, 0, 0);
		if ($ENV{'SessionLimit'} eq '') {
			$SessionLimit = $TempSession{'LIMITTIME'};
		}
		else {
			$SessionLimit = time() + $ENV{'SessionLimit'};
		}
		print SSID 'LIMITTIME=' . $SessionLimit . "\n";
		foreach $key (sort keys %$ref_session) {
			if ($key ne 'SESSIONID' and $key ne 'LIMITTIME') {
				if ($ref_session->{$key} ne '') {
					print SSID $key . '=' . $ref_session->{$key} . "\n";
				}
			}
		}
		close SSID;
		return 1;
	}
	return undef;
}

sub delay {
	my ($SessionID) = @_;
	my (%TempSession, $SessionFile, $SessionLimit, $temp);
	if ($ENV{'SessionLimit'}) {
		&limit($SessionID);
		$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
		if (-e $SessionFile) {
			open SSID, "+< $SessionFile";
			flock(SSID, 2);
			$SessionLimit = <SSID>;
			$temp = '';
			while (<SSID>) {
				$temp .= $_;
			}
			truncate(SSID, 0);
			seek(SSID, 0, 0);
			$SessionLimit = time() + $ENV{'SessionLimit'};
			print SSID 'LIMITTIME=' . $SessionLimit . "\n";
			print SSID $temp;
			close SSID;
			return 1;
		}
	}
	return undef;
}

sub delete {
	my ($SessionID) = @_;
	my ($SessionFile);
	$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
	if (-e $SessionFile) {
		unlink $SessionFile;
		if (-e $SessionFile) {
			return undef;
		}
	}
	return 1;
}

sub clear {
	my (@SessionID, $i, $ret, $flag);
	if (-e $ENV{'SessionTempDir'}) {
		opendir DIR, $ENV{'SessionTempDir'};
		@SessionID = grep(!/^\./, readdir(DIR));
		closedir DIR;
		$flag = 0;
		for ($i = 0; $i <= $#SessionID; $i++) {
			$ret = &limit($SessionID[$i]);
			if (defined $ret) {
				$flag++;
			}
		}
		if (!($temp > 0)) {
			return 1;
		}
	}
	return undef;
}

sub generate {
	my $result = '';
	for ($i = 0; $i < 32; $i++) {
		$result .= sprintf("%x", int(rand(16)));
	}
	return $result
}

sub limit {
	my ($SessionID) = @_;
	my ($SessionFile, $SessionLimit);
	$SessionFile = $ENV{'SessionTempDir'} . '/' . $SessionID;
	if (-e $SessionFile) {
		open SSID, $SessionFile;
		flock(SSID, 2);
		$SessionLimit = <SSID>;
		$SessionLimit =~ s/\x0D?\x0A?$//;
		$SessionLimit = (split(/=/, $SessionLimit, 2))[1];
		close SSID;
		if ($SessionLimit < time()) {
			$ret = &delete($SessionID);
			if ($ret) {
				return 1;
			}
			return undef;
		}
		return 1;
	}
	return undef;
}

1;
