#!/usr/bin/perl -w
#------------------------------------------------------------------------------
# $Id$
#------------------------------------------------------------------------------
# TeamFile SideMemu page CGI
#
# Copyright (c) BAYBITS LLC. All rights reserved.
#------------------------------------------------------------------------------

use strict;
use utf8;
use CGI;
use CGI::Cookie;
use Encode;
use TF::CommonUtils;
use HTML::Template;
use JSON::PP qw(decode_json);

binmode(STDOUT, ":utf8");

# global
my $LOCATION      = $ENV{'TF_LOCATION'};
my $BRAND         = $ENV{'TF_BRANDNAME'};
my $TF_VERSION    = $ENV{'TF_VERSION'}  || '';
my $EXTEND_MENU_N = $ENV{'TF_EXTENDED_SPEC_NUM'} || 0;
my $USERTYPE      = $ENV{'TF_USERTYPE'} || 'normal';
my $LANGPARAM     = TF::CommonUtils::getLangPriority();
my $TEMPLATE_BASE = './tmpl';
my $TEMPLATE_FILE = './tmpl/sidemenu.tmpl.' . $LANGPARAM;
my $EDITMAIL_NAME = $ENV{'TF_FOLDER_EDITMAIL'} || undef;
my $TF_MLEDIT     = $ENV{'TF_MLEDIT'} || 'off';
my $TF_LOGACCESS  = $ENV{'TF_LOGACCESS'} || 'off';
my $LOGACCESS     = $ENV{'TF_FOLDER_LOGACCESS'} || undef;
my $TF_AUTODELETE = $ENV{'TF_AUTODELETE'} || 'off';
my $AUTODELETE    = $ENV{'TF_FOLDER_AUTODELETE'} || undef;
my $TF_LOGOPERATION = $ENV{'TF_LOGOPERATION'} || 'off';
my $LOGOPERATION    = $ENV{'TF_FOLDER_LOGOPERATION'} || undef;
my $SHOW_USERF    = $ENV{'TF_USERSHOWUSER'} || 'on';
my $SHOW_GROUPF   = $ENV{'TF_USERSHOWGROUP'} || 'on';
my $TF_LICENSEUPLOADER = $ENV{'TF_LICENSEUPLOADER'} || 'off';
my $LICENSEUPLOADER    = $ENV{'TF_FOLDER_LICENSEUPLOADER'} || undef;
my $REPORTSETTINGS = $ENV{'TF_FOLDER_REPORTSETTINGS'} || undef;
my $IS_EXTENDSYSMSG = ($ENV{'TF_EXTENDSYSMSG'} eq 'on') ? 1 : 0;
my $IS_SSL = ($ENV{'HTTPS'} && $ENV{'HTTPS'} eq 'on') ? 1 : 0;
my $SSLCERT_SEAL = 'sslcertseal.' . $LANGPARAM;
my $ENABLE_CHANGE_PASSWORD = ($ENV{'TF_DISABLE_CHANGE_PASSWORD'} eq 'on') ? 0 : 1;
my $ENABLE_SESSION = ($ENV{'TF_USESESSION'} eq 'on') ? 1 : 0;
my $TF_TOKEN = $ENV{'TF_TOKEN'} || '';
my $SUPPORTED_LANGUAGES = $ENV{'TF_SUPPORTED_LANGTAG'} || 'ja,en';
my %LANGUAGE_COOKIES = CGI::Cookie->fetch;
my $SELECTED_LANGUAGE = exists($LANGUAGE_COOKIES{'lang'}) ?
		$LANGUAGE_COOKIES{'lang'}->value : '';
my $AUDIT_ROOT = '/var/lib/teamfile/audit';
if (!$LOCATION) {
	TF::CommonUtils::showAccessDeny();
	die '';
}

my $LOCATION_NAME = $LOCATION;
$LOCATION_NAME =~ s/\///g;

main();

#
# main
#
sub main {
	my $cgi = CGI->new;
	if ($cgi && ($cgi->request_method || 'GET') eq 'POST' &&
			($cgi->param('hidCmd') || '') eq 'changeLanguage') {
		changeLanguage($cgi);
		return;
	}
	&printHTML();
}

sub changeLanguage {
	my $cgi = shift;
	my $lang = $cgi->param('lang') || '';
	my %supported = map { $_ => 1 } grep { $_ ne '' } split(/[\s,]+/, $SUPPORTED_LANGUAGES);

	if (!$TF_TOKEN || ($cgi->param('hidcsrftoken') || '') ne $TF_TOKEN ||
			($lang ne '' && !$supported{$lang})) {
		TF::CommonUtils::showAccessDeny();
		return;
	}

	my %cookie_args = (
		-name => 'lang',
		-value => $lang,
		-path => $LOCATION,
	);
	$cookie_args{-expires} = '-3y' if $lang eq '';
	my $cookie = CGI::Cookie->new(%cookie_args);

	print $cgi->redirect(
		-uri => $LOCATION,
		-status => 303,
		-cookie => $cookie,
	);
}

#
# sub
#
sub printHTML {
	my $show_filemenu = 0;
	my @filemenu = ();
	my @sslseal = ();
	if ($SHOW_USERF eq 'on') {
		push(@filemenu,
				{	uri => TF::CommonUtils::decodeUTF8($ENV{'TF_PRIVATEFOLDER_URI'} . '?WEBDAV_METHOD=PROPFIND'),
					img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_USER_IMG'}),
					dispname => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_USER'}),
					description => getMenuDescription('userfolder'),
				});
		$show_filemenu = 1;
	}
	
	if ($SHOW_GROUPF eq 'on') {
		push(@filemenu,
				{	uri => TF::CommonUtils::decodeUTF8($ENV{'TF_GROUPFOLDER_URI'} . '?WEBDAV_METHOD=PROPFIND'),
					img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_GROUP_IMG'}),
					dispname => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_GROUP'}),
					description => getMenuDescription('groupfolder'),
				});
		$show_filemenu = 1;
	}
	&getExtenedMenu('FILE', \@filemenu);

	my @personalmenu = ();
	&getExtenedMenu('PERSONAL', \@personalmenu);

	my @managementmenu = ();
	if ($USERTYPE eq 'admin' || $USERTYPE eq 'groupleader') {
		push(@managementmenu,
				{	uri => $LOCATION . '/.cgi-bin/user',
					img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_M_USERFOLDER_IMG'} || '/icons/teamfile/tf_user.gif'),
					dispname => getMenuLabel('usermenu'),
					description => getMenuDescription('usermenu'),
				},
				{	uri => $LOCATION . '/.cgi-bin/groupmembers',
					img => '/icons/teamfile/Folder-Share.png',
					dispname => getMenuLabel('groupmembers'),
					description => getMenuDescription('groupmembers'),
				});
	}
	&getExtenedMenu('MANAGEMENT', \@managementmenu);

	my @utilitymenu = (
					{	uri => TF::CommonUtils::decodeUTF8($ENV{'TF_UTILUPDATEDCLIENT_URI'} . '?WEBDAV_METHOD=PROPFIND'),
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_M_UPDATE_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_M_UPDATE'}),
						description => getMenuDescription('updatedclient'),
					});

	if ($IS_EXTENDSYSMSG eq "0") {
		push(@utilitymenu,
					{	uri => TF::CommonUtils::decodeUTF8($ENV{'TF_UTILSYSMSG_URI'}),
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_M_SYSMSG_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_M_SYSMSG'}),
						description => getMenuDescription('sysmsg'),
					});
	}

	if ($EDITMAIL_NAME && $TF_MLEDIT eq 'on' && $USERTYPE eq 'admin') {
		push(@utilitymenu,
					{	uri => $LOCATION . '/.cgi-bin/mailsetting',
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_EDITMAIL_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($EDITMAIL_NAME),
						description => getMenuDescription('mailsetting'),
					});
	}
	if (($TF_LOGACCESS eq 'on' || $TF_LOGACCESS eq 'on-all') && $USERTYPE eq 'admin') {
		push(@utilitymenu,
					{	uri => $LOCATION . '/.cgi-bin/loglist',
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_LOGACCESS_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($LOGACCESS),
						description => getMenuDescription('loglist'),
					});
	}
	if ($TF_AUTODELETE eq 'on' && ($USERTYPE eq 'admin' || $USERTYPE eq 'groupleader')) {
		push(@utilitymenu,
					{	uri => $LOCATION . '/.cgi-bin/autodelsetting',
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_AUTODELETE_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($AUTODELETE),
						description => getMenuDescription('autodelsetting'),
					});
	}
	if ($TF_LOGOPERATION eq 'on' && ($USERTYPE eq 'admin' || $USERTYPE eq 'normal' || $USERTYPE eq 'groupleader')) {
		push(@utilitymenu,
					{	uri => $LOCATION . '/.cgi-bin/oploglist',
						img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_LOGOPERATION_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($LOGOPERATION),
						description => getMenuDescription('oploglist'),
					});
	}
	if ($USERTYPE eq 'admin') {
		push(@utilitymenu,
					{   uri => $LOCATION . '/.cgi-bin/reportsettings',
					    img => TF::CommonUtils::decodeUTF8($ENV{'TF_FOLDER_REPORTSETTINGS_IMG'}),
						dispname => TF::CommonUtils::decodeUTF8($REPORTSETTINGS),
						description => getMenuDescription('reportsettings'),
					});
	}
	if ($USERTYPE eq 'admin') {
		my %audit_status = getAuditMenuStatus();
		push(@utilitymenu,
					{	uri => $LOCATION . '/.cgi-bin/audit',
						img => '/icons/teamfile/Bill.png',
						dispname => getMenuLabel('audit'),
						description => getMenuDescription('audit'),
						%audit_status,
					});
	}
	&getExtenedMenu('UTILITY', \@utilitymenu);

	if ($IS_SSL) {
		my $fname = getPage($SSLCERT_SEAL);
		my $line;
		if (open(HTML, "<$fname")) {
			binmode(HTML, ":utf8");
			while ($line = <HTML>) {
				push (@sslseal, { line => $line } );
			}
			close(HTML);
		}
	}

	if (open(TEMPLATE, "<$TEMPLATE_FILE")) {
		binmode(TEMPLATE, ":utf8");
		my $htmltmpl = HTML::Template->new(	filehandle => *TEMPLATE,
											case_sensitive => 1,
											die_on_bad_params => 0);
		if ($htmltmpl) {
			$htmltmpl->param(	brandname => $BRAND,
								tfversion => $TF_VERSION,
								location => $LOCATION,
								logoimg => $ENV{'TF_FOLDER_TOPPAGE_IMG'},
								showfilemenu => $show_filemenu,
								filemenu => \@filemenu,
								personalmenu => \@personalmenu,
								showmanagementmenu => (scalar(@managementmenu) > 0) ? 1 : 0,
								managementmenu => \@managementmenu,
								loginname => TF::CommonUtils::decodeUTF8($ENV{'TF_USERNAME'}),
								chpassimg => $ENV{'TF_FOLDER_M_USERFOLDER_IMG'},
								utilitymenu => \@utilitymenu,
								sslseal => \@sslseal,
								enablechpass => $ENABLE_CHANGE_PASSWORD,
								enablesession => $ENABLE_SESSION,
								token => $TF_TOKEN,
								langJaSelected => ($SELECTED_LANGUAGE eq 'ja') ? 1 : 0,
								langEnSelected => ($SELECTED_LANGUAGE eq 'en') ? 1 : 0,
								isAdmin => ($USERTYPE eq 'admin') ? 1 : 0,
								);
			print "Content-type: text/html; charset=UTF-8\n\n";
			print $htmltmpl->output;
		}
		close(TEMPLATE);
	}
	else {
		TF::CommonUtils::showTemplateOpenErr($LANGPARAM);
	}
}

#
sub getExtenedMenu {
	my $menutype   = shift || 'unknown';
	my $paramarray_ref = shift || undef;

	if ($EXTEND_MENU_N == 0 || $menutype eq 'unknown' || !$paramarray_ref) {
		return;
	}

	for my $i(0 .. $EXTEND_MENU_N) {
		my $line = $ENV{"TF_EXTENDED_SPEC$i"};
		if ($line) {
			my ($mtype, $uri, $iconuri, $displayname, $usertype) = split(/,/, $line);
			if ($uri && $iconuri && $displayname) {
				if (!$mtype) {
					$mtype = 'UTILITY';
				}
				if (!$usertype) {
					$usertype = 'admin';
				}

				if ($menutype eq $mtype && $usertype =~ /$USERTYPE/) {
					push(@{$paramarray_ref},
										{	uri => TF::CommonUtils::decodeUTF8($uri),
											img => TF::CommonUtils::decodeUTF8($iconuri),
											dispname => TF::CommonUtils::decodeUTF8($displayname),
											description => '',
										});
				}
			}
		}
	}
}

sub getMenuDescription {
	my $key = shift || '';

	my %desc_ja = (
		userfolder     => '自分専用の保存領域',
		groupfolder    => 'グループ共有の保存領域',
		updatedclient  => 'クライアント更新ファイル',
		sysmsg         => '画面表示メッセージ',
		mailsetting    => '通知メールと送信設定',
		loglist        => '利用状況とアクセス履歴',
		autodelsetting => '保持期間と自動削除条件',
		oploglist      => 'ユーザ操作の記録',
		reportsettings => '定期レポート送信条件',
		lcuploader     => 'ライセンスファイル反映',
		chpass         => 'ログイン用パスワード',
		logout         => '現在のログイン終了',
		usermenu       => '管理可能ユーザの一覧',
		groupmembers   => 'グループ所属ユーザの管理',
		adminsettings  => '管理者向け各種設定',
		audit          => '運用リスクの点検',
	);

	my %desc_en = (
		userfolder     => 'Private storage',
		groupfolder    => 'Shared group storage',
		updatedclient  => 'Client update files',
		sysmsg         => 'Screen messages',
		mailsetting    => 'Mail and SMTP settings',
		loglist        => 'Access history',
		autodelsetting => 'Retention rules',
		oploglist      => 'User operation history',
		reportsettings => 'Scheduled report rules',
		lcuploader     => 'License file import',
		chpass         => 'Login password',
		logout         => 'End current session',
		usermenu       => 'Manageable user list',
		groupmembers   => 'Group member management',
		adminsettings  => 'Administrator settings',
		audit          => 'Operational risk checks',
	);

	return $LANGPARAM eq 'ja' ? ($desc_ja{$key} || '') : ($desc_en{$key} || '');
}

sub getMenuLabel {
	my $key = shift || '';

	my %label_ja = (
		usermenu => 'ユーザ',
		groupmembers => 'グループ管理',
		audit => '監査',
	);
	my %label_en = (
		usermenu => 'Users',
		groupmembers => 'Group Management',
		audit => 'Audit',
	);

	return $LANGPARAM eq 'ja' ? ($label_ja{$key} || '') : ($label_en{$key} || '');
}

sub getAuditMenuStatus {
	my $location = normalizeAuditLocation($LOCATION);
	return () if !$location;

	my $critical_count = 0;
	for my $type ('user', 'file', 'oplog') {
		my $audit = readAuditJson($location, $type);
		next if !$audit;
		my $issues = $audit->{'issues'} || [];
		if (ref($issues) eq 'ARRAY') {
			for my $issue (@{$issues}) {
				next if !$issue || ref($issue) ne 'HASH';
				$critical_count++ if ($issue->{'level'} || '') eq 'critical';
			}
		}
	}

	return () if $critical_count <= 0;
	return (
		hasAlert   => 1,
		alertLevel => 'critical',
		alertCount => $critical_count,
		alertLabel => getAuditAlertLabel('critical', $critical_count),
	);
}

sub readAuditJson {
	my ($location, $type) = @_;
	return undef if !$location || !$type;

	my $audit_file = $AUDIT_ROOT . '/' . $location . '/latest-' . $type . '-audit.json';
	return undef if !-f $audit_file;

	my $json = '';
	my $fh;
	if (!open($fh, '<', $audit_file)) {
		return undef;
	}
	# decode_json expects UTF-8 bytes and performs the decoding itself.
	binmode($fh);
	{
		local $/;
		$json = <$fh>;
	}
	close($fh);

	my $audit = eval { decode_json($json) };
	return undef if $@ || !$audit || ref($audit) ne 'HASH';
	return $audit;
}

sub normalizeAuditLocation {
	my $location = shift || '';
	$location =~ s/^\/+//;
	$location =~ s/\/+$//;
	return '' if $location eq '';

	# Audit result paths are per-location directories. Keep the value path-safe.
	$location =~ s/[^A-Za-z0-9_.-]/_/g;
	return $location;
}

sub getAuditAlertLabel {
	my $level = shift || '';
	my $count = shift || 0;

	my %label_ja = (
		critical => '重要',
		warn     => '警告',
		caution  => '注意',
	);
	my %label_en = (
		critical => 'Critical',
		warn     => 'Warning',
		caution  => 'Caution',
	);

	my $label = $LANGPARAM eq 'ja' ? ($label_ja{$level} || '') : ($label_en{$level} || '');
	return $label . ' ' . $count;
}

sub getPage {
	my $name = shift || '';
	my $fname;

	# (1) (location).$name
	$fname = $TEMPLATE_BASE . "/". $LOCATION_NAME . "." . $name;
	if (-f $fname) {
		return $fname;
	}

	# (2) $name
	$fname = $TEMPLATE_BASE . "/" . $name;

	return $fname;
}
