# -----------------------------------------------------------------------------
# TL::Session - セッションの管理を行う
# -----------------------------------------------------------------------------
package TL::Session;
use strict;
use warnings;
use TL;

sub _POST_REQUEST_HOOK_PRIORITY() { -2_000_000 } # 順序は問わない

# このクラスは次のようなクッキーまたはクエリにデータを保存する。
# * HTTP側  : クッキー名 SID + グループ名 / クエリ名 SID + グループ名
# * HTTPS側 : クッキー名 SIDS + グループ名 / クエリ名 SIDS + グループ名

# -------------------------------------------------------
# セッションデータの入出力は次のようになる。
#
# [出力時]
#   [1: クッキーを使う場合]
#     TL::FilterがSet-Cookie:ヘッダを出力する。
#     この時、iniパラメータ"cookie" / "securecookie"で指定されたグループ名のTL::Cookieが使われる。
#   [2: クエリを使う場合]
#     TL::Filterが$SAVEに加える。
#
# [入力時]
#   [1: クッキーを使う場合]
#     TL::InputFilterがdecodeCgi中に$TL->getCookieし、その中にセッションデータがあり、
#     且つTL::TinySessionが有効になっていれば、$TL->getTinySession->_setSessionDataする。
#   [2: クエリを使う場合]
#     TL::InputFilterがdecodeCgi中にクエリ内にセッションデータを見付けた場合、
#     TL::TinySessionが有効になっているなら、$TL->getTinySession->_setSessionDataする。

our %_instance;

1;

sub isHttps {
	$ENV{HTTPS} and $ENV{HTTPS} eq 'on';
}

sub get {
	my $this = shift;

	if(!defined($this->{sid})) {
		$this->_createSid;
	}

	$this->{sid};
}

sub renew {
	my $this = shift;

	$this->discard;
	$this->get;
}

sub discard {
	my $this = shift;

	if(defined($this->{sid})) {
		$this->__removeSid($this->{sid});
	}
	$this->__reset;

	$this;
}

sub setValue {
	my $this = shift;
	my $value = shift;

	if((!$this->isHttps) && ($this->{mode} eq 'double' || $this->{mode} eq 'https')) {
		die __PACKAGE__."#setValue, we can't modify session while we are using '$this->{mode}' mode and not in the https.\n";
	}

	if($this->{setvaluewithrenew}) {
		$this->discard;
		$this->{data} = $value;
		$this->get;
	} else {
		if(defined($this->{sid})) {
			$this->{data} = $value;
			$this->{updatetime} = 0; # アップデートを行わせる
			$this->__updateSession;
		} else {
			$this->{data} = $value;
			$this->get;
		}
	}

	$this;
}

sub getValue {
	my $this = shift;

	$this->{data};
}

sub getSessionInfo {
	my $this = shift;
	my $issecure = shift;

	if(!defined($issecure)) {
		$issecure = $this->isHttps
	}

	(($issecure ? 'SIDS' : 'SID') . $this->{group}, $this->{sid}, ($issecure ? $this->{checkvalssl} : $this->{checkval}));
}

sub _createSid {
	my $this = shift;

	$this->{checkval} = '_' x 19; # 64bit整数は 1844 6744 0737 0955 1615 まで
	$this->{checkval} =~ s/_/int(rand(10))/eg;
	$this->{checkvalssl} = '_' x 19; # 64bit整数は 1844 6744 0737 0955 1615 まで
	$this->{checkvalssl} =~ s/_/int(rand(10))/eg;

	$this->{sid} = $this->__createSid($this->{checkval}, $this->{checkvalssl}, $this->{data});
}

sub __createSid {
	my $this = shift;
	my $checkval = shift;
	my $checkvalssl = shift;
	my $data = shift;

	my $sid;

	my $DB = $TL->getDB($this->{dbgroup});

	my $type = $DB->getType;
	if($type eq 'mysql') {
		eval {
			$DB->execute(\$this->{dbset} => qq{
				REPLACE INTO $this->{sessiontable}
					VALUES (NULL, ?, ?, ?, now())
			}, $checkval, $checkvalssl, $data);
			$sid = $DB->getDbh($this->{dbset})->{'mysql_insertid'};
		};
		if($@) {
			die __PACKAGE__."#__createSid, cannot create sid. [$@]\n";
		}
	} else {
		die __PACKAGE__."#__createSid, the type of DB [$this->{dbgroup}] is [$type], which is not supported.\n";
	}

	if($sid) {
		$TL->log(__PACKAGE__, "Created new session sid [$sid] on the DB [$this->{dbgroup}][$this->{sessiontable}].");
	} else {
		die __PACKAGE__."#__createSid, cannot create sid.\n";
	}

	$sid;
}

sub __removeSid {
	my $this = shift;
	my $sid = shift;

	my $DB = $TL->getDB($this->{dbgroup});

	my $type = $DB->getType;
	if($type eq 'mysql') {
		eval {
			$DB->execute(\$this->{dbset} => qq{
				DELETE FROM $this->{sessiontable}
					WHERE sid = ?
			}, $sid);
		};
	} else {
		die __PACKAGE__."#__removeSid, the type of DB [$this->{dbgroup}] is [$type], which is not supported.\n";
	}

	$TL->log(__PACKAGE__, "Remove session sid [$sid] on the DB [$this->{dbgroup}][$this->{sessiontable}].");

	$sid;
}

sub __prepareSessionTable {
	my $this = shift;

	my $DB = $TL->getDB($this->{dbgroup});

	eval {
		$DB->execute(\$this->{readdbset} => qq{SELECT * FROM $this->{sessiontable} LIMIT 0});
	};
	if($@) {
		# テーブルが無いので作る。
		my $type = $DB->getType;
		if($type eq 'mysql') {
			eval {
				$DB->execute(\$this->{dbset} => qq{
					CREATE TABLE $this->{sessiontable} (
						sid           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
						checkval      BIGINT UNSIGNED NOT NULL,
						checkvalssl   BIGINT UNSIGNED NOT NULL,
						data          BIGINT UNSIGNED,
						updatetime    TIMESTAMP NOT NULL,
						PRIMARY KEY (sid)
					) AUTO_INCREMENT = 4294967296
					AVG_ROW_LENGTH = 20
					MAX_ROWS = 300000000
				});
			};
		} else {
			die __PACKAGE__."#__prepareSessionTable, the type of DB [$this->{dbgroup}] is [$type], which is not supported.\n";
		}

		$TL->log(__PACKAGE__, "Created table [$this->{sessiontable}] on the DB [$this->{dbgroup}].");
	}

	$this;
}

sub _init {
	# TL#startCgiによって呼ばれるクラスメソッド。
	my $class = shift;
	my $groups;
	if(ref($_[0]) eq 'ARRAY') {
		$groups = shift;
	} elsif(!ref($_[0])) {
		$groups = [ @_ ];
	} else {
		my $ref = ref($_[0]);
		die "TL::Session#_init, ARG[1]: group name is Wrong-Ref. [$ref]\n";
	}

	foreach my $group (@$groups) {
		$_instance{$group} = TL::Session->__new($group);

		# postRequest時に古いデータを消す。
		$TL->setHook(
			'postRequest',
			_POST_REQUEST_HOOK_PRIORITY,
			sub {
				$_instance{$group}->__reset;
			},
		);
	}

	undef;
}

sub __new {
	my $class = shift;
	my $group = shift;
	my $this = bless {} => $class;

	$this->{group} = defined $group ? $group : 'Session';

	$this->{mode} = $TL->INI->get($this->{group} => 'mode', 'double');

	my $timeout = $TL->INI->get($this->{group} => 'timeout','30min');
	$this->{timeout_period} = $TL->parsePeriod($timeout);

	my $updateinterval = $TL->INI->get($this->{group} => 'updateinterval','10min');
	$this->{updateinterval_period} = $TL->parsePeriod($updateinterval);

	$this->{setvaluewithrenew} = $TL->INI->get($this->{group} => 'setvaluewithrenew', 1);

	$this->__reset;

	# モードチェック
	if($this->{mode} eq 'https') {
		if(!$this->isHttps) {
			die __PACKAGE__."#__new, the 'https' mode of Session can't be used while we are not in the https.\n";
		}
	} elsif($this->{mode} eq 'http') {
		# 常に利用可能
	} elsif($this->{mode} eq 'double') {
		# 常に利用可能
	} else {
		die __PACKAGE__."#__new, invalid mode: [$this->{mode}]\n";
	}

	$this->{dbgroup} = $TL->INI->get($this->{group} => 'dbgroup');
	$this->{dbgroup} or die __PACKAGE__."#new, dbgroup is not set.\n";
	$this->{dbset} = $TL->INI->get($this->{group} => 'dbset');
	$this->{dbset} or die __PACKAGE__."#new, dbset is not set.\n";
	$this->{readdbset} = $TL->INI->get($this->{group} => 'readdbset', $this->{dbset});
	$this->{sessiontable} = $TL->INI->get($this->{group} => 'sessiontable', 'tl_session_' . $this->{group});

	$this->__prepareSessionTable;

	$this;
}

sub __reset {
	my $this = shift;

	$this->{sid} = undef;
	$this->{data} = undef;
	$this->{checkval} = undef;
	$this->{checkvalssl} = undef;
	$this->{updatetime} = undef; # 新規セッションを作成した場合はundefのまま
}


sub _getInstance {
	# TL#getSessionやTL::Filterによって呼ばれるクラスメソッド。
	my $class = shift;
	my $group = shift;

	defined $group or $group = 'Session';

	if($_instance{$group}) {
		$_instance{$group};
	} else {
		die "TL#getSession, the Session of $group group is not in use. ".
			"Specify [-Session => '(group)'] at the call of TL#startCgi if you want to use this.\n";
	}
}

sub _getInstanceGroups {
	# TL::Filterなどによって呼ばれるクラスメソッド。
	my $class = shift;

	return keys %_instance;
}

sub _getRawCookie {
	my $this = shift;
	my $opts = { @_ };		# secure => 1 or 0

	my $group;
	if($opts->{secure}) {
		$group = $TL->INI->get($this->{group} => 'securecookie', 'SecureCookie');
	} else {
		$group = $TL->INI->get($this->{group} => 'cookie', 'Cookie');
	}

	my $cookie = $TL->getRawCookie($group);
	if($opts->{secure} && !$cookie->_isSecure) {
		die __PACKAGE__."#_getRawCookie, cookie group [$group] is not secure.".
			" We can't use it for secure part of session.\n";
	} elsif(!$opts->{secure} and $cookie->_isSecure) {
		die __PACKAGE__."#_getRawCookie, cookie group [$group] is secure.".
		" We can't use it for insecure part of session.\n";
	}
	$cookie;
}

sub _setSessionDataToCookies {
	# クッキーを使用する場合に，TL::Filter より呼び出される．
	# 必要に応じてセッションデータをCookieにsetする。
	my $this = shift;

	if($this->isHttps) {
		if($this->{mode} eq 'https' || $this->{mode} eq 'double') {
			# https側
			my $cookie = $this->_getRawCookie(secure => 1);

			if(defined($this->{sid})) {
				$this->__updateSession(secure => 1);
				my $s = join('.', $this->{sid}, $this->{checkvalssl});
				$cookie->set('SIDS' . $this->{group} => $s);
			} else {
				$cookie->delete('SIDS' . $this->{group});
			}
		}
		if($this->{mode} eq 'http' || $this->{mode} eq 'double') {
			my $cookie = $this->_getRawCookie(secure => 0);
			if(defined($this->{sid})) {
				my $s = join('.', $this->{sid}, $this->{checkval});
				$cookie->set('SID' . $this->{group} => $s);
			} else {
				$cookie->delete('SID' . $this->{group});
			}
		}
	} else {
		if($this->{mode} eq 'http' || $this->{mode} eq 'double') {
			# http側
			my $cookie = $this->_getRawCookie(secure => 0);

			if(defined($this->{sid})) {
				$this->__updateSession(secure => 0);
				my $s = join('.', $this->{sid}, $this->{checkval});
				$cookie->set('SID' . $this->{group} => $s);
			} else {
				$cookie->delete('SID' . $this->{group});
			}
		} else {
			die __PACKAGE__."#_setSessionDataToCookies, session mode is https.".
				" We can't use it for insecure part of session.\n";
		}
	}

	$this;
}

sub _getSessionDataFromCookies {
	# クッキーを使用する場合に，TL::InputFilter より呼び出される．
	# クッキー中にセッションデータがあれば、それを読む。
	my $this = shift;

	if ($this->{mode} eq 'http' || ((!$this->isHttps) && $this->{mode} eq 'double')) {
		my $cookie = $this->_getRawCookie(secure => 0);

		if(my $s = $cookie->get('SID' . $this->{group})) {
			# http側
			my ($sid, $checkval) = split(/\./, $s);
			$this->__setSession($sid, $checkval, secure => 0);
		}
	}

	if($this->{mode} eq 'https' || ($this->isHttps and $this->{mode} eq 'double')) {
		my $cookie = $this->_getRawCookie(secure => 1);

		if(my $s = $cookie->get('SIDS' . $this->{group})) {
			# https側
			my ($sid, $checkval) = split(/\./, $s);
			$this->__setSession($sid, $checkval, secure => 1);
		}
	}

	$this;
}

sub _setSessionDataToForm {
	# フォームを使用する場合に，TL::Filter より呼び出される．
	# 必要に応じてセッションデータをFormにsetする。
	my $this = shift;
	my $form = shift;

	if($this->isHttps) {
		if(defined($this->{sid})) {
			$this->__updateSession(secure => 1);
			my $s = join('.', $this->{sid}, $this->{checkvalssl});
			$form->set('SIDS' . $this->{group} => $s);
		}
	} else {
		if(defined($this->{sid})) {
			$this->__updateSession(secure => 0);
			my $s = join('.', $this->{sid}, $this->{checkval});
			$form->set('SID' . $this->{group} => $s);
		}
	}

	$this;
}

sub _getSessionDataFromForm {
	# フォームを使用する場合に，TL::InputFilter より呼び出される．
	# フォーム中にセッションデータがあれば、それを読む。
	my $this = shift;
	my $form = shift;

	if($this->{mode} eq 'http' || ((!$this->isHttps) && $this->{mode} eq 'double')) {
		if(my $s = $form->get('SID' . $this->{group})) {
			# http側
			my ($sid, $checkval) = split(/\./, $s);
			$this->__setSession($sid, $checkval, secure => 0);
		}
	}

	if($this->{mode} eq 'https' || ($this->isHttps && $this->{mode} eq 'double')) {
		if(my $s = $form->get('SIDS' . $this->{group})) {
			# https側
			my ($sid, $checkval) = split(/\./, $s);
			$this->__setSession($sid, $checkval, secure => 1);
		}
	}

	$this;
}

sub __setSession {
	# セッションの存在確認をし，問題がなければデータをセットする．
	my $this = shift;
	my $sid = shift;
	my $checkval = shift;
	my %opts = @_;

	my $DB = $TL->getDB($this->{dbgroup});

	my $type = $DB->getType;
	if($type eq 'mysql') {
		eval {
			my $colname = ($opts{secure} ? 'checkvalssl' : 'checkval');

			my $sessiondata = $DB->selectAllArray(\$this->{readdbset} => qq{
				SELECT data, UNIX_TIMESTAMP(updatetime), checkval, checkvalssl
					FROM $this->{sessiontable}
					WHERE sid = ? AND $colname = ?
			}, $sid, $checkval);

			if(!scalar(@$sessiondata)) {
				$TL->log(__PACKAGE__, "Invalid session. session is not found. sid [$sid] checkval [$checkval] on the DB [$this->{dbgroup}][$this->{sessiontable}].");
			} elsif(time - $sessiondata->[0][1] > $this->{timeout_period}) {
				$TL->log(__PACKAGE__, "Invalid session. session is timeout. sid [$sid] checkval [$checkval] updatetime [$sessiondata->[0][1]] on the DB [$this->{dbgroup}][$this->{sessiontable}].");
			} else {
				$this->{sid} = $sid;
				$this->{data} = $sessiondata->[0][0];
				$this->{updatetime} = $sessiondata->[0][1];
				$this->{checkval} = $sessiondata->[0][2];
				$this->{checkvalssl} = $sessiondata->[0][3];
			}
		};
	} else {
		die __PACKAGE__."#__setSession, the type of DB [$this->{dbgroup}] is [$type], which is not supported.\n";
	}

	if(defined $this->{sid}) {
		my $datalog = (defined($this->{data}) ? $this->{data} : '(undef)');
		$TL->log(__PACKAGE__, "Valid session data read. secure [$opts{secure}] sid [$this->{sid}] checkval [$this->{checkval}] checkvalssl [$this->{checkvalssl}] data [$datalog] updatetime [$this->{updatetime}] on the DB [$this->{dbgroup}][$this->{sessiontable}].");
	} else {
		$TL->log(__PACKAGE__, "Valid session data didn't read. secure [$opts{secure}] sid [$sid] checkval [$checkval] on the DB [$this->{dbgroup}][$this->{sessiontable}].");
	}

	$this;
}

sub __updateSession {
	my $this = shift;
	my %opts = @_;

	if(!defined($this->{updatetime})) {
		return $this;
	}

	if(time - $this->{updatetime} < $this->{updateinterval_period}) {
		return $this;
	}

	my $DB = $TL->getDB($this->{dbgroup});

	my $type = $DB->getType;
	if($type eq 'mysql') {
		eval {
			my $colname = ($opts{secure} ? 'checkvalssl' : 'checkval');

			my $sessiondata = $DB->execute(\$this->{readdbset} => qq{
				UPDATE $this->{sessiontable}
					SET updatetime = now(), data = ?
					WHERE sid = ?
			}, $this->{data}, $this->{sid});
		};
	} else {
		die __PACKAGE__."#__updateSession, the type of DB [$this->{dbgroup}] is [$type], which is not supported.\n";
	}

	$this->{updatetime} = time;

	my $datalog = (defined($this->{data}) ? $this->{data} : '(undef)');
	$TL->log(__PACKAGE__, "Update session. sid [$this->{sid}] data [$datalog] on the DB [$this->{dbgroup}][$this->{sessiontable}].");

	$this;
}


__END__

=encoding utf-8

=head1 NAME

TL::Session - セッション

=head1 SYNOPSIS

  $TL->startCgi(
      -DB      => 'DB',
      -Session => 'Session',
      -main        => \&main,
  );

  sub main {
      my $session = $TL->getSession('Session');

      my $oldValue = $session->getValue;
      
      $session->setValue(12345);

      ...
  }

=head1 DESCRIPTION

64bit符号無し整数値の管理機能を持ったセッション管理クラス。

セッションは64bit符号無し整数以外のデータを取り扱えない為、その他のデータを管理したい場合は、
セッションキーを用い別途管理する必要がある。 

セッションキーは、 L<出力フィルタ|TL/"出力フィルタ"> に L<TL::Filter::HTML>
を使用している場合は L<クッキー|TL::Cookie> に、 L<TL::Filter::MobileHTML>
の場合は L<クエリ|TL::Form> に、それぞれ挿入される。

また、 L<入力フィルタ|TL/"入力フィルタ"> に L<TL::InputFilter::HTML>
を使用している場合は L<クッキー|TL::Cookie> から、L<TL::Filter::MobileHTML>
の場合は L<クエリ|TL::Form> から、それぞれ読み取られる。

Sessionは L<DB|TL::DB> を使用してセッションの管理を行う。

プログラム本体とDB接続を共有するため、以下の点に注意しなければならない。

=over 4

=item セッションの操作は、トランザクション中及びテーブルロック中には行わない。

=item コンテンツの出力操作は、トランザクション中及びテーブルロック中には行わない。

=back

=head2 METHODS

=over 4

=item C<< $TL->getSession >>

  $session = $TL->getSession($group)

TL::Session オブジェクトを取得。
引数には L<ini|TL::Ini> で設定したグループ名を渡す。省略可能。

このメソッドは、 L<TL#startCgi|TL/"startCgi">
の呼び出し時に C<< -Session => '(Iniグループ名)' >> で指定されたグループのセッションが有効化
されていなければ C<die> する。

引数省略時は 'Session' グループが使用される。

=item C<< isHttps >>

  $session->isHttps

現在のリクエストがhttpsなら1を、そうでなければundefを返す。

  if ($session->isHttps) {
      ...
  }

=item C<< get >>

  $sid = $session->get

ユニークなセッションキーを取得する。

セッションキーは64bit符号無し整数値となる。

Perlでは通常32bit整数値までしか扱えないため、セッションキーを数値として扱ってはならない。

セッションが存在しなければ、新規に発行する。

セッションの発行は常に行え、double モード時の非SSL側からの get メソッド呼び出しでもセッションは設定される。
ただし、SSL側からアクセスした際にセッションが無効になるため、その時にセッションIDは再作成される。

このメソッドの呼び出しは、コンテンツデータを返す前に行わなければならない。

=item C<< renew >>

  $sid = $session->renew

新しくユニークなセッションキーを発行し、取得する。

以前のセッションキーが存在した場合、そのセッションキーは無効となる。
また、以前のセッションに保存されていた値も破棄される。

このメソッドの呼び出しは、コンテンツデータを返す前に行わなければならない。

=item C<< discard >>

  $session->discard

現在のセッションキーを無効にする。

このメソッドの呼び出しは、コンテンツデータを返す前に行わなければならない。

=item C<< setValue >>

  $session->setValue($value)

セッションに値を設定する。

設定できる値は '64bit符号無し整数' のみ。
その他のデータを管理したい場合は、セッションキーを用いて別途実装する必要がある。

doubleモードの場合は、SSL起動時の場合に限り、両方のセッションに書き込まれる。
doubleモードで非SSL側からこのメソッドを使ってセッションを書換えようとした場合、
httpsモードで非SSL側から書き換えようとした場合は C<die> する。

このメソッドの呼び出しは、コンテンツデータを返す前に行わなければならない。

=item C<< getValue >>

  $value = $session->getValue

セッションから値を取得する。

セッションが存在しない場合は undef を返す。

=item C<< getSessionInfo >>

  ($name, $sid, $checkval) = $session->getSessionInfo

セッション情報を取得する。

クッキーやフォームにセッションを保存する際の名称、セッションキー、チェック値を返す。
チェック値は、現在のリクエストが https/http によって使用されているものが返される。
そのため、double モードの場合、現在のリクエスト状態に応じてチェック値が異なる。

セッションが存在しない場合は $sid、$checkval には undef が返る。

=back


=head2 Ini パラメータ

=over 4

=item mode

  mode = double

設定可能な値は、'http'、 'https'、 'double'のいずれか。省略可能。

デフォルトはdouble。

=over 8

=item httpモード

SSLでの保護がないセッションを利用する。http/httpsの両方で使用できるが、セッションキーはhttp側から漏洩する可能性があるため、https領域からアクセスした場合も、十分な安全性は確保できないことに注意する必要がある。

=item httpsモード

SSLでの保護があるセッションを利用する。セッションキーはhttp側からの漏洩を防ぐため、http通信上には出力されない。https側でのみセッションへのアクセスが可能。

=item doubleモード

http側とhttps側で二重にセッションを張る。
https側からのみセッションへの書き込み・破棄が行え、その際にhttp側のセッション情報も同時に書き換えられる。
http側からはhttps側からセットされたセッション情報の参照のみが出来る。

http側はセッションキー漏洩の危険性があり、十分な安全性は確保できないが、https側は十分な安全性が確保できる。http側からセッションキーが漏洩した場合でも、https領域でのアクセスは安全である。

                http領域読込    http領域書込    https領域読込   http領域書込
  httpモード    ○              ○              ○              ○
  httpsモード   C<die>             C<die>             ○              ○
  doubleモード  ○              C<die>             ○              ○

=back

=item cookie

  cookie = Cookie

http領域で使用するクッキーのグループ名を指定する。省略可能。

デフォルトは'Cookie'。

=item securecookie

https 領域で使用するクッキーのグループ名を指定する。省略可能。
secureフラグが付いていなければエラーとなる。

デフォルトは'SecureCookie'．

=item timeout

  timeout = 30 min

指定の時間経過したセッションは無効とする。L<度量衡|TL/"度量衡"> 参照。省略可能。
最短で timeout - updateinterval の時間でタイムアウトする可能性がある。

デフォルトは30min。

=item updateinterval

  updateinterval = 10 min

最終更新時刻から指定時間以上経過していたら、DBの更新時刻を更新する。L<度量衡|TL/"度量衡"> 参照。省略可能。
最短で timeout - updateinterval の時間でタイムアウトする可能性がある。

デフォルトは10min。

=item setvaluewithrenew

  setvaluewithrenew = 1

setValueした際に自動的にrenewを行うか否か。
0の場合、行わない。
1の場合、行う。

デフォルトは1。

=item dbgroup

  dbgroup = DB

使用するDBのグループ名。
L<ini|TL::Ini> で設定したグループ名を渡す。
L<TL#startCgi|TL/"startCgi"> で有効化しなければならない。

=item dbset

  dbset = W_Trans

使用する書き込み用DBセット名。
L<TL#startCgi|TL/"startCgi"> で有効化しなければならない。
L<ini|TL::Ini> で設定したグループ名を渡す。

=item readdbset

  readdbset = R_Trans

使用する読み込み用DBセット名。
L<TL#startCgi|TL/"startCgi"> で有効化しなければならない。
L<ini|TL::Ini> で設定したグループ名を渡す。

省略された場合は dbset と同じものが使用される。

=item sessiontable

  sessiontable = tl_session

セッションで使用するテーブル名。
デフォルトは tl_session_グループ名 が使用される。

=item csrfkey

  csrfkey = JLapCbI4XW7G8oEi

addSessionCheck及びhaveSessionCheckで使用するキー。
サイト毎に値を変更する必要性がある。

=back


=head1 SEE ALSO

=over 4

=item L<TL>

=item L<TL::Cookie>

=item L<TL::DB>

=item L<TL::Filter::HTML>

=item L<TL::Filter::MobileHTML>

=item L<TL::InputFilter::HTML>

=item L<TL::InputFilter::MobileHTML>

=back

=head1 AUTHOR INFORMATION

=over 4

Copyright 2006 YMIRLINK Inc. All Rights Reserved.

This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself

このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。

Address bug reports and comments to: tl@tripletail.jp

HP : http://tripletail.jp/

=back

=cut
