package SWUser;

use Digest::SHA 'sha1_base64';

#----------------------------------------
# ユーザーファイル制御
#----------------------------------------

#----------------------------------------
# コンストラクタ
#----------------------------------------
sub new {
	my ($class, $sow) = @_;
	my $self = {
		sow => $sow,
	};

	return bless($self, $class);
}

#----------------------------------------
# ユーザーデータファイル名の取得
#----------------------------------------
sub GetFNameUser {
	my $self = shift;

	my $encodename = &SWBase::EncodeURL($self->{'uid'});
	my $filename = "$self->{'sow'}->{'cfg'}->{'DIR_USER'}/$encodename.cgi";
	return $filename;
}

#----------------------------------------
# ユーザーデータラベル
#----------------------------------------
sub GetUserDataLabel {
	my @datalabel = (
		'uid',
		'pwd',				# crypt(3) ハッシュ
		'pwd_sha1',		# SHA1 ハッシュ
		'handlename',
		'url',
		'introduction',
	);
	return @datalabel;
}

#----------------------------------------
# ユーザーデータファイルを開く
#----------------------------------------
sub openuser {
	my ($self, $chklogin) = @_;

	my $filename = $self->GetFNameUser();
	my $fh = \*USER;
	if ($chklogin == 0) {
		# 後で新規作成するためエラーを出さない
		$self->{'uid'} = '';
		$self->{'pwd'} = '';
		$self->{'pwd_sha1'} = '';
		return -1 if !(-e $filename); # ファイルがない
	}

	# ユーザーデータを開く
	my $file = SWFile->new($self->{'sow'}, 'user', $fh, $filename, $self);
	$file->openfile(
		'+<',
		'ユーザーデータ',
		"[uid=$self->{'uid'}]",
	);
	$self->{'file'} = $file;

	seek($fh, 0, 0);
	my @data = <$fh>;

	my $datalabel = shift(@data);
	my @datalabel = split(/<>/, $datalabel);
	@datalabel = $self->GetUserDataLabel() if ($datalabel[0] eq '');
	@$self{@datalabel} = split(/<>/, $data[0]);

	# 移行用コード
	my @strdata = ('url', 'introduction', 'handlename','pwd_sha1');
	foreach (@strdata) {
		$self->{$_} = '' if (!defined($self->{$_}));
	}
	my @labelnew = $self->GetUserDataLabel();
	foreach (@labelnew) {
		$self->{$_} = 0 if (!defined($self->{$_}));
	}
	foreach (@strdata) {
		$self->{$_} = '' if ($self->{$_} eq $self->{'sow'}->{'DATATEXT_NONE'});
	}

	return 0;
}

#----------------------------------------
# ユーザーデータ書き込み
#----------------------------------------
sub writeuser {
	my $self = shift;
	my $sow = $self->{'sow'};
	my $fh = $self->{'file'}->{'filehandle'};

	my @strdata = ('url', 'introduction', 'handlename');
	foreach (@strdata) {
		$self->{$_} = $self->{'sow'}->{'DATATEXT_NONE'} if ($self->{$_} eq '');
	}

	truncate($fh, 0);
	seek($fh, 0, 0);
	my @datalabel = $self->GetUserDataLabel();
	print $fh join("<>", @datalabel). "<>\n";
	print $fh join("<>", map{$self->{$_}}@datalabel). "<>\n";

	$sow->{'debug'}->writeaplog($sow->{'APLOG_POSTED'}, "Edit User. [$self->{'uid'}]");

	foreach (@strdata) {
		$self->{$_} = '' if ($self->{$_} eq $self->{'sow'}->{'DATATEXT_NONE'});
	}
}

#----------------------------------------
# ユーザーデータファイルを閉じる
#----------------------------------------
sub closeuser {
	my $self = shift;
	$self->{'file'}->closefile() if (defined($self->{'file'}));
	return;
}

#----------------------------------------
# 通常認証
#----------------------------------------
sub LoginSW {
	my ($self, $chklogin) = @_;
	my $sow = $self->{'sow'};

	my $src = $sow->{'cookie'}; # クッキーからユーザーIDを取得
	$src = $sow->{'query'} if ($sow->{'outmode'} eq 'mb'); # 携帯モードの時は引数から取得
	$src = $sow->{'query'} if ($chklogin == 0); # ログイン処理の時は引数から取得
	if (!defined($src->{'uid'})) {
		$src->{'uid'} = '';
		$src->{'pwd'} = '';
		$src->{'pwd_sha1'} = '';
	}
	$src->{'uid'} =~ s/^ *//;
	$src->{'uid'} =~ s/ *$//;

	my $lengthuid = length($src->{'uid'});
	$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "ユーザIDは $sow->{'cfg'}->{'MAXSIZE_USERID'} バイト以内で入力して下さい（$lengthuid バイト）。", "uid too long.") if ($lengthuid > $sow->{'cfg'}->{'MAXSIZE_USERID'});
	$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "ユーザIDは $sow->{'cfg'}->{'MINSIZE_USERID'} バイト以上で入力して下さい（$lengthuid バイト）。", "uid too short.") if (($lengthuid < $sow->{'cfg'}->{'MINSIZE_USERID'}) && ($lengthuid != 0));

	my $lengthpwd = length($src->{'pwd'});
	$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "パスワードは $sow->{'cfg'}->{'MAXSIZE_PASSWD'} バイト以内で入力して下さい（$lengthpwd バイト）。", "pwd too long.") if ($lengthpwd > $sow->{'cfg'}->{'MAXSIZE_PASSWD'});
	$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "パスワードは $sow->{'cfg'}->{'MINSIZE_PASSWD'} バイト以上で入力して下さい（$lengthpwd バイト）。", "pwd too short.") if (($lengthpwd < $sow->{'cfg'}->{'MINSIZE_PASSWD'}) && ($lengthuid > 0));

	$self->{'uid'}  = $src->{'uid'};
	$self->{'qpwd'} = $src->{'pwd'};
	$self->{'logined'} = $self->match($chklogin);

	return;
}

#----------------------------------------
# TypeKey認証
#----------------------------------------
sub LoginTypeKey {
	my ($self, $chklogin) = @_;
	my $sow = $self->{'sow'};

	# TypeKey認証
	eval 'use Authen::TypeKey;';
	$sow->{'debug'}->raise($sow->{'APLOG_WARNING'}, "Authen::TypeKeyモジュールが見つかりません。", "Authen::TypeKey not found.") if ($@ ne '');
	my $src = $sow->{'cookie'};
	$src = $sow->{'query'} if ($chklogin == 0);
	if (!defined($src->{'sig'})) {
		$src->{'uid'} = '';
		$src->{'sig'} = '';
	}

	if ($src->{'sig'} eq '') {
		if ($chklogin == 0) {
			$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "認証データがありません。", "typekey sig not found.");
		} else {
			$self->{'logined'} = -1;
			return;
		}
	}

	my $typekey = new Authen::TypeKey;
	$typekey->token($sow->{'cfg'}->{'TOKEN_TYPEKEY'});
	my $result = $typekey->verify($src);
	$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "ユーザーIDかパスワードが間違っています。", $typekey->errstr()) if ($result ne '');
	$self->{'uid'}  = $src->{'name'};
	$self->{'nick'} = $src->{'nick'};

	$self->{'logined'} = 1;
	return;
}

#----------------------------------------
# ログイン情報の取得
#----------------------------------------
sub logined {
	my $self = shift;

	if (!defined($self->{'logined'})) {
		if ($self->{'sow'}->{'cfg'}->{'ENABLED_TYPEKEY'} > 0) {
			$self->LoginTypeKey(1);
		} else {
			$self->LoginSW(1);
		}
	}
	return $self->{'logined'};
}

#----------------------------------------
# ログイン情報の取得
#----------------------------------------
sub login {
	my $self = shift;

	if ($self->{'sow'}->{'cfg'}->{'ENABLED_TYPEKEY'} > 0) {
		$self->LoginTypeKey(0);
	} else {
		$self->LoginSW(0);
	}
	return $self->{'logined'};
}

#----------------------------------------
# パスワード照合
# [返し値]
# -1：引数のuidが値なし/引数のpwdが値なし
#     /ユーザーデータのpwdが値なし
#   　/ユーザーデータなし（$chklogin=0の時）
#  0：パスワードが違う
#  1：照合成功
#----------------------------------------
sub match {
	my ($self, $chklogin) = @_;
	my $sow = $self->{'sow'};
	
	return -1 if ($self->{'uid'} eq '');
	return -1 if ($self->{'qpwd'} eq '');
	
	$self->openuser($chklogin);
	
	my $pwmatch = 0;
	my $pwd_sha1 = sha1_base64( $self->{'uid'}.$self->{'qpwd'} );
	
	if ($self->{'pwd_sha1'} eq ''){
	# 環境依存ハッシュ
		if ($self->{'pwd'} eq '') {
			$pwmatch = -1;
		} elsif ($self->{'pwd'} eq crypt($self->{'qpwd'}, $self->{'pwd'})) {
			$pwmatch = 1;	# 認証成功
			
			# ハッシュを移行する
			$self->{'pwd'} = '';
			$self->{'pwd_sha1'} = $pwd_sha1;
			$self->writeuser();
		}
	} else {
	# 環境非依存ハッシュ
		if ( $self->{'pwd_sha1'} eq $pwd_sha1 ){
			$pwmatch = 1;	# 認証成功
		}
	}
	$self->closeuser();
	if( $pwmatch == 0 ){
		$self->resetcookie();		# クッキー削除
		$sow->{'debug'}->raise($sow->{'APLOG_NOTICE'}, "ユーザーIDかパスワードが間違っています。", "no match pass.[$self->{'uid'}]");
	}

	return $pwmatch;
}

#----------------------------------------
# クッキーデータのセット
#----------------------------------------
sub setcookie {
	my ($self, $setcookie) = @_;
	my $sow = $self->{'sow'};
	my $query = $sow->{'query'};
	if ($sow->{'cfg'}->{'ENABLED_TYPEKEY'} > 0) {
		my $nick = $query->{'nick'};
		&SWBase::JcodeConvert($sow, \$nick, 'sjis', 'utf8');
		$setcookie->{'name'} = $query->{'name'};
		$setcookie->{'nick'} = $nick;
		$setcookie->{'sig'}  = $query->{'sig'};
	} else {
		$setcookie->{'uid'} = $query->{'uid'};
		$setcookie->{'pwd'} = $query->{'pwd'};
	}

	return;
}

#----------------------------------------
# クッキーデータのリセット
#----------------------------------------
sub resetcookie {
	my ($self, $setcookie) = @_;

	my $query = $self->{'sow'}->{'query'};
	if ($self->{'sow'}->{'cfg'}->{'ENABLED_TYPEKEY'} > 0) {
		$setcookie->{'name'} = '';
		$setcookie->{'nick'} = '';
		$setcookie->{'sig'}  = '';
	} else {
		$setcookie->{'uid'} = '';
		$setcookie->{'pwd'} = '';
	}

	return;
}

#----------------------------------------
# ユーザーデータ追加
#----------------------------------------
sub createuser {
	my ($self, $uid, $qpwd) = @_;
	my $sow = $self->{'sow'};
	$self->{'uid'}  = $uid;
	$self->{'qpwd'} = $qpwd;

	$self->{'handlename'} = '';
	$self->{'url'} = '';
	$self->{'introduction'} = '';

	my $filename = $self->GetFNameUser();

	my $fh = \*USER;
	my $file = SWFile->new($self->{'sow'}, 'user', $fh, $filename, $self);
	$file->openfile(
		'>',
		'ユーザーデータ',
		"[uid=$self->{'uid'}]",
	);

	my @datalabel = $self->GetUserDataLabel();
	$self->{'pwd'} = '';
	$self->{'pwd_sha1'} = sha1_base64( $self->{'uid'}.$self->{'qpwd'} );

	print $fh join("<>", @datalabel). "<>\n";
	print $fh join("<>", map{$self->{$_}}@datalabel). "<>\n";

	$sow->{'debug'}->writeaplog($sow->{'APLOG_POSTED'}, "Add User. [$self->{'uid'}]");

	return;
}

#----------------------------------------
# ユーザーデータファイルの更新日時を得る
#----------------------------------------
sub getupdatedt {
	my $self = shift;
	my $filename = $self->GetFNameUser();

	return (stat($filename))[9];
}
#		
#		#----------------------------------------
#		# 暗号文の取得（MD5/DES）
#		#----------------------------------------
#		sub GetCrypt {
#			my $salt = &GetSalt();
#		
#			my $crypted = crypt($_[0], '$1$' . $salt . '$'); # MD5
#			$crypted = crypt($_[0], $salt) if (substr($crypted, 0, 3) ne '$1$'); # 標準（たいていDES）
#		
#			return $crypted;
#		}
#		
#		#----------------------------------------
#		# SALT の取得
#		#----------------------------------------
#		sub GetSalt {
#			my @CHARSET_BASE64 = ('.', '/', '0'..'9', 'A'..'Z', 'a'..'z');
#			my $salt;
#		
#			for ($i = 0; $i < 8; $i++) {
#				$salt .= $CHARSET_BASE64[rand(@CHARSET_BASE64)];
#			}
#			return $salt;
#		}
#		
1;
