#============================================================
# BrowseFilesObject
#============================================================
package BrowseFilesObject;
use Common;
@ISA = qw(Common);

#公開したいサブルーチン
#@EXPORT   = qw(erfc tan);
@EXPORT_OK = qw();

use strict;
use MyArchive;

#==========================================
# 大域変数
#==========================================
my $ModuleName        = "BrowseFilesObject";
my $ModuleExplanation = "Browse Files Object";

sub Name        { return $ModuleName; }
sub Explanation { return $ModuleExplanation; }

my $ChangePasswordPrivilege  = 'ChangeDownloadAccountPassword';
my $CreateHTAccessPrivilege  = 'CreateHTAccess';
my $ModifyFilePrivilege      = 'ModifyFile';
my $EditTemplatePrivilege    = 'EditTemplate';
my $UploadFilePrivilege      = 'UploadFile';
my $ShowDirTreeLinkPrivilege = 'ShowDirTreeLink';

#ZIPファイル内のファイル名文字コード
my $ZIPFileCharCode      = 'sjis';
#ダウンロードする際のファイル名文字コード
my $DownloadFileCharCode = 'sjis';

#Upload可能なファイルの指定
my $UploadFileMaxSize = 200 * 1024 * 1024;
#編集可能なファイル名
my @EdittableFileRegExp = ("\\.(html?|css|txt|csv|ini|mysql|pm|pl|bat|inf)\$");
#受付可能なMIME型と拡張子（正規表現）
#my @TypeOkList = qw(text\/[\w]+ application\/vnd.ms-excel  application\/vnd.ms-word message\/rfc822);
my @TypeOkList = ();
#my @TypeOkList = qw (text\/[\w]+ application\/octet-streatm application\/pdf 
#		   application\/msword application\/vnd.ms-powerpoint message\/rfc822);
#my @ExtOkList  = qw(csv);
my @ExtOkList  = qw ();
#my @ExtOkList  = qw (txt html htm csv xls xlsx pdf doc docx ppt pptx zip gz lha ini);

#============================================================
#　コンストラクタ、デストラクタ
#============================================================
sub new
{
	my ($module) = @_;
	my $this = {};
	bless $this;
#	$this->SUPER::new(@_);
	return $this;
}

sub DESTROY
{
	my $this = shift;
	$this->SUPER::DESTROY(@_);
}

sub Configure
{
	my ($this, $modulename, $privilege, $rootpath, $keyword,
		$pPermittedDirs, $pBrowseFiles, $pDenyFiles, 
		$pSubstitutionSource, $pSubstitutionTarget, 
		$pURLSubstitutionList, $pURLReverseSubstitutionList,
		$showdate, $showdirectory, $showparentdirectory, $showfilesize, $targetpage, %args) = @_;

	$this->{ModuleName}  = $modulename  if(defined $modulename);
	$this->{Privilege}   = $privilege  if(defined $privilege);

	$this->{RootPath} = $rootpath  if(defined $rootpath);
	$this->{Keyword}  = $keyword   if(defined $keyword);

	$UploadFileMaxSize   = $args{UploadFileMaxSize}       if(defined $args{UploadFileMaxSize});
	@EdittableFileRegExp = @{$args{pEdittableFileRegExp}} if(defined $args{pEdittableFileRegExp});
	@TypeOkList          = @{$args{pTypeOkList}}          if(defined $args{pTypeOkList});
	@ExtOkList           = @{$args{pExtOkList}}           if(defined $args{pExtOkList});

	$this->{pPermittedDirs}              = $pPermittedDirs              if(defined $pPermittedDirs);
	$this->{pBrowseFiles}                = $pBrowseFiles                if(defined $pBrowseFiles);
	$this->{pDenyFiles}                  = $pDenyFiles                  if(defined $pDenyFiles);
	$this->{pSubstitutionSource}         = $pSubstitutionSource         if(defined $pSubstitutionSource);
	$this->{pSubstitutionTarget}         = $pSubstitutionTarget         if(defined $pSubstitutionTarget);
	$this->{pURLSubstitutionList}        = $pURLSubstitutionList        if(defined $pURLSubstitutionList);
	$this->{pURLReverseSubstitutionList} = $pURLReverseSubstitutionList if(defined $pURLReverseSubstitutionList);

	$this->{ShowDate}            = $showdate            if(defined $showdate);
	$this->{ShowDirectory}       = $showdirectory       if(defined $showdirectory);
	$this->{ShowParentDirectory} = $showparentdirectory if(defined $showparentdirectory);
	$this->{ShowFileSize}        = $showfilesize        if(defined $showfilesize);
	$this->{TargetPage}          = $targetpage          if(defined $targetpage);
}

#============================================================
#　メンバー関数
#============================================================
sub GetParameterHash
{
	my ($this) = @_;
	return {};
}

sub IsOpen
{
	my ($this, $App, $pParams, $IsPrint) = @_;
	return if($this->{RootPath} eq '');
	return $App->HasPrivilege($this->{Privilege}, $IsPrint);
}

sub ShowMenu
{
	my ($this, $App, $pParams, $ProgramPath, $BaseOption) = @_;
	return 0;
}

sub MakeDirLink
{
	my ($this, $App, $pParams, $dir, $label) = @_;
	return '' if($dir eq '.');
	return '' if($dir eq '..' and !$pParams->{ShowParentDirectory});

	my $TargetStr = '';
	$TargetStr = " target=\"$this->{TargetPage}\" " if($this->{TargetPage} ne '');
	$dir = Utils::URLEncode($dir);
	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => "$this->{ModuleName}::ShowPage",
			PrevAction => $pParams->{Action},
			Path       => $dir,
			ShowParentDirectory => $this->{ShowParentDirectory},
			ShowDirectory       => $this->{ShowDirectory},
			ShowDate            => $this->{ShowDate},
			ShowFileSize        => $this->{ShowFileSize},
		},
		[qw(+Action Path Key SubMenuFunction UserSN IsChildFrame FrameMode ShowParentDirectory ShowDirectory ShowDate ShowFileSize)], 
		undef, undef, 0);
	my $s = "<a href=\"$pParams->{ScriptPath}?$Option\"$TargetStr><b>$label</b></a>";
	return $s;
}

sub MakeEditLink
{
	my ($this, $App, $pParams, $path, $label, $pAllowedExtRegExps) = @_;
	my $Passed = 0;
	for(my $i = 0 ; $i < @$pAllowedExtRegExps ; $i++) {
		my $p = $pAllowedExtRegExps->[$i];
#$App->print("$i:[$p][$path]\n");
		if($path =~ /$p/i) {
			$Passed = 1;
		}
	}
	return '' if(!$Passed);
	return '' if($path eq '.');
	return '' if($path eq '..' and !$pParams->{ShowParentDirectory});

	$path =~ s/^file:\/\///i;
Jcode::convert(\$path, $pParams->{FileSystemCharCode});
	$path = Utils::URLEncode($path);
	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => "EditTemplate::EditFile",
			PrevAction => $pParams->{Action},
			Path       => $path,
			ShowParentDirectory => $this->{ShowParentDirectory},
			ShowDirectory       => $this->{ShowDirectory},
			ShowDate            => $this->{ShowDate},
			ShowFileSize        => $this->{ShowFileSize},
		},
		[qw(+Action Path Key UserSN IsChildFrame FrameMode ShowParentDirectory ShowDirectory ShowDate ShowFileSize)], 
		"_blank", "_blank", 0);
	return "<a href=\"$pParams->{ScriptPath}?$Option\" target=\"_blank\"><b>$label</b></a>"
}

sub MakeFileLink
{
	my ($this, $App, $pParams, $path, $label) = @_;

	$path =~ s/^file:\/\///i;
Jcode::convert(\$path, $pParams->{FileSystemCharCode});
#$App->print("path: [$path]\n");
	my $RegExpRootDir = Utils::RegExpQuote($pParams->{WebDocumentRootDir});
	if($path =~ /^$RegExpRootDir/) {
#	if($path !~ /cgi-bin/i and $pParams->{WebDocumentRoot} ne '') {
		if($path =~ s/^$RegExpRootDir//i) {
			$path = "/$path" if($path !~ /^\//);
Jcode::convert(\$label, $pParams->{FileSystemCharCode});
#$path = Utils::URLEncode($path, 1);
#$App->print("p[$path]\n");
			return "<a href=\"$path\" target=\"_blank\"><b>$label</b></a>"
		}
	}

	$path = Utils::URLEncode($path);
	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => "$this->{ModuleName}::Download",
			PrevAction => $pParams->{Action},
			Path       => $path,
			ShowParentDirectory => $this->{ShowParentDirectory},
			ShowDirectory       => $this->{ShowDirectory},
			ShowDate            => $this->{ShowDate},
			ShowFileSize        => $this->{ShowFileSize},
		},
		[qw(+Action Path Key SubMenuFunction UserSN IsChildFrame FrameMode ShowParentDirectory ShowDirectory ShowDate ShowFileSize)], 
		"_blank", "_blank", 0);
	return "<a href=\"$pParams->{ScriptPath}?$Option\" target=\"_blank\"><b>$label</b></a>"
}

sub MakeDirTreeLink
{
	my ($this, $App, $pParams, $dir) = @_;

Jcode::convert(\$dir, "euc-jp", $pParams->{FileSystemCharCode});
#$App->print("path: [$dir]\n");
	$dir =~ s/\\/\//g;
	my @a = Utils::Split("\\/", $dir);
Jcode::convert(\$dir, $pParams->{FileSystemCharCode}, "euc-jp");
for(my $i = 0 ; $i < @a ; $i++) {
	Jcode::convert(\$a[$i], $pParams->{FileSystemCharCode}, "euc-jp");
}
	my $Path = $a[0];
	if($Path =~ /:$/) {
		$Path .= "/";
	}
	else {
		$Path = "/$Path";
	}
	my $s = '';
	my $label = $Path;
	for(my $i = 0 ; $i < @a ; $i++) {
		my ($IsPermitted, $BrowseDir, $FileRegExp, $DirRegExp, $ss, $st) = $this->IsPermitted($App, $pParams, $Path);
		if(!$IsPermitted) {
			$Path  = Utils::MakePath($Path,  $a[$i+1], '/', 0);
#			$label = Utils::MakePath($label, $a[$i+1], '/', 0);
#			$label = '$Root'. '/' . $a[$i+1];
			$label = 'ROOTDIR';
			next;
		}
		if(-d $Path) {
#			my $link = ($i == 0)? $this->MakeDirLink($App, $pParams, $Path, '$Root/') :
#					      $this->MakeDirLink($App, $pParams, $Path, $label);
			my $link = $this->MakeDirLink($App, $pParams, $Path, $label);
			$s .= '/' . $link;
		}
		elsif(-f $Path) {
			$s .= '/' . $this->MakeFileLink($App, $pParams, $Path, $label);
		}
		else {
		}
		$Path = Utils::MakePath($Path, $a[$i+1], '/', 0);
		$label = $a[$i+1];
	}
	$s =~ s/\///;
	$s = "$dir <font color=\"#FF0000\">(not permitted)</font>" if($s eq '');
	return $s;
}

sub PrintFileFunc
{
	my ($this, $App, $pParams, $path) = @_;

	my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($path);
	my $link = $this->MakeFileLink($App, $pParams, $path, $filename);
	$App->PrintRawHTML("  <tr>\n");
	$App->PrintRawHTML("    <td>$link</td>\n");
	my $dir = "$drive$directory";
	$dir =~ s/\\/\//g;
	$dir =~ s/\/$//;
	my $link = $this->MakeDirLink($App, $pParams, $dir, $dir);
	$App->PrintRawHTML("    <td>$link</td>\n");
	$App->PrintRawHTML("  </tr>\n");
}

sub SearchRecursive
{
	my ($this, $App, $pParams, $dir, $fmask, $func) = @_;

	my ($IsPermitted, $pd, $bf, $bd, $ss, $st) = $this->IsPermitted($App, $pParams, $dir);
	return if(!$IsPermitted);

	my $path = Utils::MakePath($dir, "*", '/', 0);
	my @files = $App->GetFileList($pParams, $path, 1);
	for(my $i = 0 ; $i < @files ; $i++) {
#$App->print("d: [$files[$i]]\n");
		next if($files[$i] =~ /[\\\/]\.\.?$/);
		next if(!-d $files[$i]);
		my ($IsPermitted, $BrowseDir, $FileRegExp, $DirRegExp, $ss, $st) = $this->IsPermitted($App, $pParams, $files[$i]);
		next if(!$IsPermitted);

		my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($files[$i]);
		$this->SearchRecursive($App, $pParams, $files[$i], $fmask, $func);
	}

	my $count = 0;
	my $path = Utils::MakePath($dir, '*', '/', 0);
#	my $path = Utils::MakePath($dir, $fmask, '/', 0);
Jcode::convert(\$path, $pParams->{FileSystemCharCode});
	my @files = $App->GetFileList($pParams, $path, 1);
	for(my $i = 0 ; $i < @files ; $i++) {
#$App->print("f: [$files[$i]]\n");
		next if(!-f $files[$i]);
		my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($files[$i]);
		next if($filename !~ /$fmask/i);
		my ($IsPermitted, $BrowseDir, $FileRegExp, $DirRegExp, $ss, $st) = $this->IsPermitted($App, $pParams, $files[$i]);
		next if(!$IsPermitted);

		if($count == 0) {
#			$App->PrintRawHTML("<table border=\"1\">\n");
			$count++;
		}
		&$func($files[$i]);
	}
	if($count > 0) {
#		$App->PrintRawHTML("</table>\n");
	}
}

sub SearchFile
{
	my ($this, $App, $pParams) = @_;
	return if(!$this->IsOpen($App, $pParams, 0));

	my $SearchWord = $pParams->{SearchWord};
	$App->H2("Search for [$SearchWord]　\n");

Jcode::convert(\$SearchWord, $pParams->{FileSystemCharCode});
#	$SearchWord = "*$SearchWord*" if($SearchWord !~ /\*/);

	my $path = ($pParams->{Path})? $pParams->{Path} : $this->{RootPath};
if(!-d $path) {
	$path = Utils::URLDecode($path);
	Jcode::convert(\$path, $pParams->{FileSystemCharCode});
}
#$App->print("Search from [$path]\n");
	$pParams->{Path} = $path;
	$this->ShowSearchForm($App, $pParams);

	$this->{MultiFunctionTableJScript} = JSParams->new()->GetMultiFunctionalTableString('.TableTest3', sortList => '[[1,1]]', excludeColumns => '[2,3]')
			if($this->{MultiFunctionTableJScript});
	$this->{pMultiFunctionTableConfig} = {
		class => 'tablesorter'
		} if(!$this->{pMultiFunctionTableConfig});

	$App->PrintRawHTML($this->{MultiFunctionTableJScript}) if($this->{MultiFunctionTableJScript});
	my $pHash = $this->{pMultiFunctionTableConfig};

	$App->BeginTable(1, undef, %$pHash);
	$App->BeginTableHeader();
	$App->BeginRow();
	$App->PrintRawHTML("    <th><b>File</b></th>\n");
	$App->PrintRawHTML("    <th><b>Folder</b></th>\n");
	$App->EndRow();
	$App->EndTableHeader();

	$this->SearchRecursive($App, $pParams, $path, $SearchWord, 
				sub { $this->PrintFileFunc($App, $pParams, @_); });

	$App->EndTableBody();
	$App->EndTable();

	$App->HR();
	$this->ShowPage($App, $pParams);
}

sub ShowSearchForm
{
	my ($this, $App, $pParams) = @_;

	my $path = $pParams->{Path};
	$path = Utils::URLEncode($path);
	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => "$this->{ModuleName}::SearchFile",
			PrevAction => $pParams->{Action},
			Path       => $path,
		},
		[qw(+Action Key Path UserSN IsChildFrame FrameMode)], $this->{TargetPage}, $pParams->{TargetFrame}, 1);

	my $SearchStr = $App->mlText('en' => 'Search file', 'jp' => '検索ファイル名');
#Jcode::convert(\$SearchStr, $pParams->{WebCharCode});
	$App->BeginForm($pParams, $pParams->{ScriptPath}, "POST", "_self", 1);
	$App->PrintRawHTML("  <b>$SearchStr:</b> <input type=\"text\" name=\"SearchWord\" value=\"$pParams->{SearchWord}\" size=\"20\">\n");
	$App->PrintFormButton("submit",  $App->GetPhrase('Execute search'));
	$App->PrintRawHTML("$Option\n");
	$App->EndForm(undef, undef, undef, undef);
}

sub UpdateFileList
{
	my ($this, $App, $pParams, $RootPath) = @_;
	$App->SendNotificationForPrivilegedAccessMail($App, $pParams);
	return if(!$App->HasPrivilege($ModifyFilePrivilege, 1));

my $Kara = '～'; #'～';
my $KaraZIPCharCode = $Kara;
Jcode::convert(\$Kara, $pParams->{FileSystemCharCode}, 'utf-8');#$pParams->{ScriptCharCode});
Jcode::convert(\$KaraZIPCharCode, $ZIPFileCharCode);
	$RootPath = $this->{RootPath} if($RootPath eq '');

	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => $pParams->{Action}, #"${ModuleName}::ShowPage", #$this->{NextAction},
			PrevAction => $pParams->{Action},
#			Path       => Utils::URLEncode($RootPath),
		},
		[qw(+Action Path Key SubMenuFunction IsChildFrame FrameMode)],
			undef, undef, 1,
		);

	my $Path       = Utils::URLDecode($pParams->{Path});
	my $PathRegExp = Utils::RegExpQuote($Path);

	if($pParams->{Delete} ne '') {
$App->H2($App->GetPhrase('Delete files') . "\n");
		foreach my $key (sort keys %$pParams) {
			next if($key !~ /^CheckFile-(.*)$/);
			next if($pParams->{$key} ne 'ON');

			my $key     = $1;
			my $newname = $pParams->{"NewFile-$key"};
			if($newname ne '') {
				$App->H3("Error: New name [$newname] is not null. Delete will not be executed.\n");
				return;
			}

#			my $Path0 = Utils::URLDecode($pParams->{Path});
			my $path  = Utils::MakePath($RootPath, Utils::URLDecode($key), '/', 0);
			if(!-d $path) {
				if(Utils::DeleteFile($path) == 1) {
					$App->H3("[$path] deleted.\n");
				}
				else {
					$App->H3("Error: [$path] could not be deleted.\n");
				}
			}
			else {
				if(Utils::DeleteDirectory($path) == 1) {
					$App->H3("Directory [$path] deleted.\n");
				}
				else {
					$App->H3("Error: Directory [$path] could not be deleted.\n");
				}
			}
		}
	}
	elsif($pParams->{Rename} ne '') {
$App->H2($App->GetPhrase('Rename files') . "\n");
		foreach my $key (sort keys %$pParams) {
			next if($key !~ /^CheckFile-(.*)$/);
			next if($pParams->{$key} ne 'ON');

			my $key     = $1;
			my $newname = $pParams->{"NewFile-$key"};
			next if($newname eq '');

			my $fname   = Utils::URLDecode($key);
			my $inpath  = Utils::MakePath($RootPath, $fname, '/', 0);

			Utils::URLDecode($newname);
			Jcode::convert(\$newname, $pParams->{FileSystemCharCode}, $pParams->{WebCharCode});
			my $newpath = Utils::MakePath($Path, $newname, '/', 0);
#$App->print("renmae [$inpath] to [$newpath]\n");
			if(Utils::MoveFile($inpath, $newpath) == 1) {
				$App->H3("[$inpath] was renamed to [$newpath].\n");
			}
			else {
				$App->H3("Error: [$inpath] could not be renamed to [$newpath].\n");
			}
		}
	}
	elsif($pParams->{CreatePasswordedZip} ne '') {
$App->H2($App->GetPhrase('Create Passworded Zip files') . "\n");
		$App->BeginForm($pParams, $pParams->{ScriptPath}, "POST", "_self", 1);
		$App->PrintFormButton("ExecuteCreatePasswordedZip",  "Execute");
		$App->print("\n");
		$App->PasswordTextbox('ZipPassword', 30, $pParams->{ZipPassword},   '<b>Password:</b> ');
		$App->print("\n");
		foreach my $key (sort keys %$pParams) {
			next if($key !~ /^CheckFile-(.*)$/);
			my $key0 = $1;
			my $fname = Utils::URLDecode($key0);
			next if($pParams->{$key} ne 'ON');

			my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($fname);
			$fname = $filename;
			my $cs = $App->BuildCheckboxString($key, $pParams->{$key}, '');
			$App->PrintRawHTML($cs);
			$App->print("$fname");
			my $NewFileKey = "NewFile-$key0";
			if($pParams->{$NewFileKey} eq '') {
				$pParams->{$NewFileKey} = $fname;
				$pParams->{$NewFileKey} = Deps::ReplaceExtension($pParams->{$NewFileKey}, '.zip');
			}
			$App->Textbox($NewFileKey, 30, $pParams->{$NewFileKey}, ' => ');
			$App->print("\n");
		}
		$App->PrintRawHTML("$Option\n");
		$App->EndForm(undef, undef, undef, undef);
	}
	elsif($pParams->{ExecuteCreatePasswordedZip} ne '') {
$App->H2("Execute CreatePasswordedZipFiles\n");
		if($pParams->{ZipPassword} eq '') {
			$App->H3("Error: Password must not be null.\n");
			$pParams->{CreatePasswordedZip} = 'ON';
			delete $pParams->{ExecuteCreatePasswordedZip};
			$App->HR();
			$this->UpdateFileList($App, $pParams, $RootPath);
			return;
		}
		foreach my $key (sort keys %$pParams) {
			next if($key !~ /^CheckFile-(.*)$/);
			my $key0 = $1;
			my $fname = Utils::URLDecode($key0);
			next if($pParams->{$key} ne 'ON');

			my $NewFileKey = "NewFile-$key0";
			my $NewFile    = $pParams->{$NewFileKey};
#Jcode::convert(\$fname,   $pParams->{FileSystemCharCode});
Jcode::convert(\$NewFile, $pParams->{FileSystemCharCode}, $pParams->{WebCharCode});
			$fname =~ s/\\/\//g;
			my $SourcePath = Utils::MakePath($RootPath, $fname,   '/', 0);
			my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($SourcePath);
			$directory =~ s/\\/\//g;
			my $TargetPath = Utils::MakePath("$drive$directory", $NewFile, '/', 0);
#$App->print("S: $SourcePath => ");
			$SourcePath =~ s/^$PathRegExp//;
			$SourcePath =~ s/^\///;
			$TargetPath =~ s/^$PathRegExp//;
			$TargetPath =~ s/^\///;
#$App->print("$SourcePath\n");
			my $SourcePathZIPCharCode = $SourcePath;
Jcode::convert(\$SourcePathZIPCharCode, $ZIPFileCharCode, $pParams->{FileSystemCharCode});
$SourcePathZIPCharCode =~ s/$KaraZIPCharCode/-/g if($^O ne 'MSWin32');
$SourcePathZIPCharCode =~ s/\?/-/g;
			my $ArchivePath = $SourcePath;
			my $IsCopied = 0;
			if($SourcePathZIPCharCode ne $SourcePath) {
#$SourcePathZIPCharCode = Utils::URLEncode($SourcePathZIPCharCode);
				$App->PrintRawHTML("Try to use encoded [$pParams->{FileSystemCharCode} to $ZIPFileCharCode] filename.<br>\n");
				my $SourceFullPath = Utils::MakePath($RootPath, $SourcePath,   '/', 0);
				my $TargetFullPath = Utils::MakePath($RootPath, $SourcePathZIPCharCode, '/', 0);
				if(-e $TargetFullPath) {
					$App->PrintRawHTML("[$TargetFullPath] exists. Use the original file.<br>\n");
				}
				elsif(Utils::CopyFile($SourceFullPath, $TargetFullPath) > 0) {
					$ArchivePath = $SourcePathZIPCharCode;
					$IsCopied = 1;
					$App->PrintRawHTML("Copy [$SourceFullPath] to [$TargetFullPath] succeeded.<br>\n");
				}
				else {
					$App->PrintRawHTML("Copy [$SourceFullPath] to [$TargetFullPath] failed.<br>\n");
				}
			}
			my $ret = MyArchive->new()->MakePasswordedZIP(
				$TargetPath, $Path, [$SourcePathZIPCharCode], $pParams->{ZipPassword}, '-D', $App);
#				$NewFile, $this->{RootPath}, [$fname], $pParams->{ZipPassword}, '-D', $App);
#				$TargetPath, $pParams->{RootPath}, [$SourcePath], $pParams->{ZipPassword}, '-D', $App);
			if($IsCopied) {
				my $TargetFullPath = Utils::MakePath($RootPath, $SourcePathZIPCharCode, '/', 0);
				if(Utils::DeleteFile($TargetFullPath) > 0) {
					$App->PrintRawHTML("[$TargetFullPath] has been deleted.<br>\n");
				}
				else {
					$App->PrintRawHTML("Delete [$TargetFullPath] failed.<br>\n");
				}
			}
			if($ret) {
				$App->H3("Convert [$SourcePath] to [$TargetPath] succeeded.\n");
			}
			else {
				$App->H3("Error: Convert [$SourcePath] to [$TargetPath] failed.\n");
			}
		}
	}
	elsif($pParams->{BurstZip} ne '') {
$App->H2($App->GetPhrase('Burst Zip files') . "\n");
		foreach my $key (sort keys %$pParams) {
			next if($key !~ /^CheckFile-(.*)$/);
			my $key0 = $1;
			my $fname = Utils::URLDecode($key0);
			next if($pParams->{$key} ne 'ON');

			my $SourcePath = Utils::MakePath($RootPath, $fname,   '/', 0);
			$SourcePath =~ s/^$PathRegExp//;
			$SourcePath =~ s/^\///;
			my $ret = MyArchive->new()->BurstZIP($SourcePath, $Path, '', $App);
			if($ret) {
				$App->H3("Burst [$SourcePath] succeeded.\n");
			}
			else {
				$App->H3("Error: Burst [$SourcePath] failed.\n");
			}
		}
	}
	elsif($pParams->{CreateDir} ne '') {
$App->H2($App->GetPhrase('Create Dir') . "\n");
		my $newname = $pParams->{NewPath};
		Utils::URLDecode($newname);
		Jcode::convert(\$newname, $pParams->{FileSystemCharCode}, $pParams->{WebCharCode});
		my $newpath = Utils::MakePath($Path, $newname, '/', 0);
#$App->print("mkdir [$newpath]\n");
		if(Utils::CreateDirectory($newpath, '/', 0) == 1) {
				$App->H3("Directory [$newpath] has been created.\n");
		}
		else {
				$App->H3("Error: Directory [$newpath] could not be created.\n");
		}
	}
	elsif($pParams->{CreateFile} ne '') {
$App->H2($App->GetPhrase('Create File') . "\n");
		my $newname = $pParams->{NewPath};
		Utils::URLDecode($newname);
		Jcode::convert(\$newname, $pParams->{FileSystemCharCode}, $pParams->{WebCharCode});
		my $newpath = Utils::MakePath($Path, $newname, '/', 0);
		if(-e $newpath) {
			$App->H3("Error: [$newpath] already exists.\n");
		}
		else {
			my $out = new JFile($newpath, 'w');
			if($out) {
				$App->H3("File [$newpath] has been created.\n");
				$out->Close();
			}
			else {
				$App->H3("Error: File [$newpath] could not be created.\n");
			}
		}
	}
	elsif($pParams->{Upload} ne '') {
$App->H2($App->GetPhrase('Upload file') . "\n");
		my $SourceFileName = $pParams->{UploadFilePath};
		$SourceFileName =~ s/\\/\//g;
		if($SourceFileName =~ /\//) {
			my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($SourceFileName);
			$SourceFileName = $filename;
		}
		if($SourceFileName eq '') {
			$App->H3("Error: Source path [$SourceFileName] is not null.\n");
			return;
		}

		my $ServerFileName = $SourceFileName;
#my $code = Jcode::getcode($Kara);
#$App->print("code: $code Kara:[$Kara] S[$ServerFileName]\n");
		$ServerFileName =~ s/$Kara/-/g if($^O ne 'MSWin32');
		my $TargetPath = Utils::MakePath($Path, $ServerFileName, '/', 0);
Jcode::convert(\$TargetPath, $pParams->{FileSystemCharCode});
#$App->print("[$SourceFileName][$ServerFileName][$TargetPath]\n");
		my %args = (
#			FileFieldName => 'UploadFilePath',
			UploadFilePath    => $SourceFileName,
			UploadFileMaxSize => $UploadFileMaxSize,
			ServerFileName    => $ServerFileName,
			ServerFilePath    => $TargetPath,
			pTypeOkList       => \@TypeOkList,
			pExtOkList        => \@ExtOkList,
			);
		my $ret = $App->MyHTMLApplication::UploadFile($pParams, $App->Args()->CGIForm(), %args);
		if($ret ne 'OK') {
			$App->H3("Error: File [$SourceFileName] could not be uploaded to [$TargetPath] with ret=$ret.\n");
			return;
		}
		else {
#Jcode::convert(\$SourceFileName, $pParams->{WebCharCode});
Jcode::convert(\$TargetPath,     $pParams->{WebCharCode});
			$App->H3("File [$SourceFileName] has been uploaded to [$TargetPath].\n");
		}
	}
}

sub ShowPage
{
	my ($this, $App, $pParams, $RootPath, $pPrevFunc, $pPostFunc, %args) = @_;
	$App->SendNotificationForPrivilegedAccessMail($App, $pParams);
	return if(!$this->IsOpen($App, $pParams, 0));

my $ZenkakuSpace = '　';
Jcode::convert(\$ZenkakuSpace, $pParams->{FileSystemCharCode});

	my $CanShowDirTreeLinkPrivilege = $App->HasPrivilege($ShowDirTreeLinkPrivilege, 0);
	my $CanModifyFile               = $App->HasPrivilege($ModifyFilePrivilege, 0);
	my $CanEdit                     = $CanModifyFile and $App->HasPrivilege($EditTemplatePrivilege, 0);
	my $CanUpload                   = $App->HasPrivilege($UploadFilePrivilege, 0);
	
	$RootPath = $this->{RootPath} if(!defined $RootPath);
	$RootPath =~ s/\\/\//g;
	$RootPath = Utils::ReduceDirectory($RootPath);
	my $ParentDirectory = $RootPath;
	$ParentDirectory    =~ /^(.*)\/[^\/]+\/?$/;
	$ParentDirectory    = $1;

	my $pFilterFunc              = $args{pFilterFunc};
	$pParams->{NextAction}       = "${ModuleName}::ShowPage"       if(!defined $pParams->{NextAction});
	$this->{NextAction}          = $pParams->{NextAction}          if(defined $pParams->{NextAction});
	$this->{ShowDate}            = $pParams->{ShowDate}            if(defined $pParams->{ShowDate});
	$this->{ShowDirectory}       = $pParams->{ShowDirectory}       if(defined $pParams->{ShowDirectory});
	$this->{ShowParentDirectory} = $pParams->{ShowParentDirectory} if(defined $pParams->{ShowParentDirectory});
	$this->{ShowFileSize}        = $pParams->{ShowFileSize}        if(defined $pParams->{ShowFileSize});
	$this->{TargetPage}          = $pParams->{TargetPage}          if(defined $pParams->{TargetPage});
	$this->{ShowHTAccess}        = $pParams->{ShowHTAccess}        if(defined $pParams->{ShowHTAccess});
	my $TargetStr = '';
	$TargetStr = " target=\"$this->{TargetPage}\" " if($this->{TargetPage} ne '');

	&$pPrevFunc($App, $pParams) if($pPrevFunc);

	if($pParams->{Delete} ne '' or $pParams->{Rename} or $pParams->{CreateDir} or 
	   $pParams->{CreateFile} ne '' or $pParams->{Upload} or
	   $pParams->{CreatePasswordedZip} ne '' or $pParams->{BurstZip} ne '' or $pParams->{ExecuteCreatePasswordedZip} ne '') {
		$this->UpdateFileList($App, $pParams);
		$App->HR();
	}

	if($CanShowDirTreeLinkPrivilege) {
		my $link = $this->MakeDirTreeLink($App, $pParams, $RootPath);
Jcode::convert(\$link, $pParams->{WebCharCode});
		$App->mlPrintRawHTML('en' => "<H2>Browse [$link]</H2>", 'jp' => "<H2>[$link]を表示</H2>");
	}
	
	my ($IsPermitted, $BrowseDir, $FileRegExp, $DirRegExp, $ss, $st) = $this->IsPermitted($App, $pParams, $RootPath);
	if(!$IsPermitted) {
		$App->H2("Error: Browsing [$RootPath] is not permitted.");
		return;
	}

	$BrowseDir =~ s/\\/\//g;
	my $FullPath = Utils::MakePath($RootPath, "*", '/', 0);
Jcode::convert(\$FullPath, $pParams->{FileSystemCharCode});

	if($FullPath =~ m|^//|) {
#		$FullPath =~ s/\//\\/g;
	}
	elsif($FullPath =~ /\s/) {
		$FullPath = "\"$FullPath\"";
	}
#Jcode::convert(\$FullPath, $pParams->{FileSystemCharCode});

	my $ShowFiles = Utils::MakePath($BrowseDir, $FileRegExp, '/', 0);
Jcode::convert(\$ShowFiles, $pParams->{WebCharCode});
	if($CanShowDirTreeLinkPrivilege) {
		$App->mlPrintRawHTML('en' => "<H5>Approved for [$ShowFiles]", 'jp' => "<H5>[$ShowFiles]が許可されています");
	}

	$this->ShowSearchForm($App, $pParams);

	$App->BeginForm($pParams, $pParams->{ScriptPath}, "POST", "_self", 1);
	if($CanModifyFile) {
		$App->Textbox('NewPath', 50, $pParams->{NewPath});
		$App->PrintFormButton("CreateDir",  $App->GetPhrase('Create Dir')) if($this->{ShowDirectory});
		$App->PrintFormButton("CreateFile", $App->GetPhrase('Create File'));
		$App->print("\n");
		if($CanUpload) {
			$App->FileSubmitbox('UploadFilePath', 50, $pParams->{UploadFilePath}, '');
			$App->PrintFormButton("Upload", $App->GetPhrase('Upload'));
			$App->print("\n");
		}
		$App->PrintFormButton("Delete",              $App->GetPhrase('Delete'));
		$App->PrintFormButton("Rename",              $App->GetPhrase('Rename'));
		$App->PrintFormButton("CreatePasswordedZip", $App->GetPhrase('Create Passworded Zip'));
		$App->PrintFormButton("BurstZip",            $App->GetPhrase('Burst Zip'));
		$App->print("\n");
	}

	my $black1 = "<font color=\"#000000\">";
	my $black2 = "</font>";
	my @TableDef = (
			["${black1}<b>". $App->GetPhrase('Date')           . "</b>${black2}"],
			["${black1}<b>". $App->GetPhrase('File name')      . "</b>${black2}"],
			["${black1}<b>". $App->GetPhrase('Size (KB)')      . "</b>${black2}"],
			["${black1}<b>". $App->GetPhrase('New file name')  . "</b>${black2}"],
			);
	delete $TableDef[3] if(!$CanModifyFile);

#	$App->PrintRawHTML($args{MultiFunctionTableJScript}) if($args{MultiFunctionTableJScript});
#	$this->{MultiFunctionTableJScript} = $args{MultiFunctionTableJScript};
#	my $pHash = ($args{pMultiFunctionTableConfig})? $args{pMultiFunctionTableConfig} : {};
#	$this->{pMultiFunctionTableConfig} = $pHash;
	$this->{MultiFunctionTableJScript} = JSParams->new()->GetMultiFunctionalTableString('.TableTest3', sortList => '[[1,0]]', excludeColumns => '[2,3]')
			if(!$this->{MultiFunctionTableJScript});
	$this->{pMultiFunctionTableConfig} = {
		class => 'tablesorter'
		} if(!$this->{pMultiFunctionTableConfig});
	$App->PrintRawHTML($this->{MultiFunctionTableJScript}) if($this->{MultiFunctionTableJScript});
	my $pHash = $this->{pMultiFunctionTableConfig};

	$App->BeginTable(1, undef, %$pHash);
	$App->BeginTableHeader();
	$App->BeginRow();
	for(my $i = 0 ; $i < @TableDef ; $i++) {
		$App->PrintRawHTML("    <th><b>$TableDef[$i]->[0]</b></th>\n");
	}
	$App->EndRow();
	$App->EndTableHeader();

	$App->BeginTableBody();

	my @files = $App->GetFileList($pParams, $FullPath, 1);
#$App-print("a:", join(',', @files), "\n");
#
#親ディレクトリィを表示
#
	if($this->{ShowDirectory} and $this->{ShowParentDirectory}) {
		my ($IsPermittedPD, $BrowseDirPD, $FileRegExpPD, $DirRegExpPD, $ssPD, $stPD) 
				= $this->IsPermitted($App, $pParams, $ParentDirectory);
#$App-print("ParentDir: [$ParentDirectory] IsPermitted=$IsPermittedPD]\n");

		if($IsPermittedPD) {
			my $WebPD = $ParentDirectory;
Jcode::convert(\$WebPD, $pParams->{FileSystemCharCode});
			my $mdate = Utils::GetWriteDate($WebPD);
			my $date  = Utils::BuildDateString($mdate);

			$App->BeginRow();
			$App->TableCell($date) if($this->{ShowDate});
#			$App->PrintRawHTML("$date: ") if($this->{ShowDate});
			my $ParentFolderLabel = $App->mlText('en' => '[Parent Folder]', 'jp' => '[親フォルダー]');
			my $link = $this->MakeDirLink($App, $pParams, $WebPD, $ParentFolderLabel);
			$App->TableCell($link . '　');
#			$App->PrintRawHTML("<td>$link</td>\n");
#			$App->PrintRawHTML("$link<br>\n");
			if($this->{ShowFileSize}) {
				$App->TableCell("0");
			}
			$App->EndRow();
		}
	}

#その他のファイルを表示
	my $RegExpRootPath = Utils::RegExpQuote($this->{RootPath});
	for my $f (@files) {
#$App->print("f[$f]\n");
#親ディレクトリィの表示は済んでいるので、飛ばす
		if($pFilterFunc) {
			next if(!&$pFilterFunc($this, $App, $pParams, $f));
		}
		else {
		}
		next if($f =~ /\/\.{1,2}$/);
#		next if($f =~ /\/.$/);
#		next if($f =~ /\/..$/ and !$pParams->{ShowParentDirectory});
		next if(!$this->{ShowHTAccess} and $f =~ /^\.ht/);

		my ($IsPermitted, $pd, $bf, $bd, $ss, $st) = $this->IsPermitted($App, $pParams, $f);
		next if(!$IsPermitted);

		my $EncodedPath = $f;
		$EncodedPath =~ s/^$RegExpRootPath//i;
		$EncodedPath =~ s/^\///;
		$EncodedPath = Utils::URLEncode($EncodedPath);
		my $OriginalPath = $f;

#Jcode::convert(\$f, "euc-jp", $pParams->{FileSystemCharCode});
#		$f =~ /.*\/([^\/]+)$/;
#		my $fname = $1;
		my ($drive, $directory, $fname, $ext, $lastdir, $filebody) = Deps::SplitFilePath($f);
#Jcode::convert(\$f, $pParams->{FileSystemCharCode}, "euc-jp");
		my $mdate = Utils::GetWriteDate($f);
		my $date  = Utils::BuildDateString($mdate);

		$App->BeginRow();

		$fname =~ s/$ss/$st/ig if($ss ne '');
#その他のディレクトリィを表示
		if(($this->{ShowDirectory} and -d $f) and ($DirRegExp eq '' or $fname =~ /$DirRegExp/i) ) {
#$VarName, $CurVal, $label, $charcode, %args
			my $key = 'CheckFile-' . $EncodedPath;
			if($CanModifyFile) {
				my $cs = $App->BuildCheckboxString($key, $pParams->{$key}, '');
				$date = $cs . $date;
			}
			$App->TableCell($date) if($this->{ShowDate});
#			$App->PrintRawHTML("$date: ") if($this->{ShowDate});
# Windowsでは、ここで変換すると"講演"が化ける
if($^O eq 'MSWin32') {
}
else {
	Jcode::convert(\$fname, $pParams->{WebCharCode}, $pParams->{FileSystemCharCode});
}
#$f = Utils::URLEncode($f);
			my $link = $this->MakeDirLink($App, $pParams, $f, "<b>[$fname]</b>");
#Jcode::convert(\$link, $pParams->{WebCharSet}, $pParams->{FileSystemCharSet});
			$App->TableCell($link . $ZenkakuSpace);
#			$App->PrintRawHTML("$link<br>\n");
			if($this->{ShowFileSize}) {
				$App->TableCell("0");
			}
			if($CanModifyFile) {
				$key = 'NewFile-' . $EncodedPath;
				my $s = $App->BuildTextboxString($key, 10, $pParams->{$key});
				$App->TableCell($s);
			}
		}
		if((-f $f) and ($FileRegExp eq '' or $fname =~ /$FileRegExp/i) ) {
			if($fname !~ /^\//) {
				$f = "file://$f";
			}
			else {
				$f = "file:$f";
			}
			$this->URLSubstitution($App, $pParams, $f);
#MakeFielLinkでEncodeしている
#			$f = Utils::URLEncode($f);
#$VarName, $CurVal, $label, $charcode, %args
			my $key = 'CheckFile-' . $EncodedPath;
			if($CanModifyFile) {
				my $cs = $App->BuildCheckboxString($key, $pParams->{$key}, '');
				$date = $cs . $date;
			}
			$App->TableCell($date) if($this->{ShowDate});
#			$App->print("$date: ") if($this->{ShowDate});
			my $link     = $this->MakeFileLink($App, $pParams, $f, "<b>$fname</b>");

			my $EditLink = ($this->{ShowHTAccess})?
				$this->MakeEditLink($App, $pParams, $f, 'edit', [@EdittableFileRegExp, "^\\.ht"]) :
				$this->MakeEditLink($App, $pParams, $f, 'edit', [@EdittableFileRegExp]);
			$link = "$link ($EditLink)" if($CanEdit and $EditLink ne '');
			$App->TableCell($link . $ZenkakuSpace);

#			$App->PrintRawHTML("$link\n");
			if($this->{ShowFileSize}) {
				my ($size, $unit) = Utils::GetFileSize($OriginalPath, 'kb', "%12.3f");
#				my ($size, $unit) = Utils::GetFileSize($OriginalPath, 'auto');
				$App->TableCell($size);
#				$App->TableCell("$size $unit");
#				$App->PrintRawHTML(" ($size $unit)");
			}
			if($CanModifyFile) {
				$key = 'NewFile-' . $EncodedPath;
				my $s = $App->BuildTextboxString($key, 10, $pParams->{$key});
				$App->TableCell($s);
			}
#			$App->PrintRawHTML("<br>\n");
		}
		$App->EndRow();
	}

	&$pPostFunc($App, $pParams) if($pPostFunc);

	$App->EndTableBody();
	$App->EndTable();

	if(!-d $RootPath) {
		my ($drive, $directory, $fname, $ext, $lastdir, $filebody) = Deps::SplitFilePath($RootPath);
		$RootPath = "$drive$directory";
	}
	my ($Option, $target) = $App->GetCGIParameters(
		{
			Action     => $this->{NextAction},
			PrevAction => $pParams->{Action},
			Path       => Utils::URLEncode($RootPath),
		},
		[qw(+Action Path Key SubMenuFunction IsChildFrame FrameMode -ValidAbstractOnly -ValidProgramOnly)],
		undef, undef, 1,
		);
	$App->PrintRawHTML("$Option\n");
	$App->EndForm(undef, undef, undef, undef);
}

sub ShowCreateHTAccessPage
{
	my ($this, $App, $pParams, $Path) = @_;
	return if(!$this->IsOpen($App, $pParams, 0));
	return if(!$App->HasPrivilege($CreateHTAccessPrivilege, 0));
#$App->print("P[$Path][$pParams->{WebDocumentRoot}]\n");
	return if($Path !~ /^$pParams->{WebDocumentRoot}/);

	my ($Option, $target) = $App->GetCGIParameters(
			{
				Action                 => ($pParams->{NextAction})? $pParams->{NextAction} : "${ModuleName}::CreateHTAccess",
				Path                   => Utils::URLEncode($Path),
				PrevAction             => $pParams->{Action},
			},
			[qw(+Action NextAction Path Key SubMenuFunction IsChildFrame FrameMode)],
			undef, undef, 1,
		);
	$pParams->{PasswdFileName} = '.htpasswd'   if($pParams->{PasswdFileName} eq '');
	$pParams->{AuthName}       = 'SecretFiles' if($pParams->{AuthName} eq '');
	$App->BeginForm($pParams, $pParams->{ScriptPath}, "POST", "_self", 0);
	$App->PrintFormButton("CreateHTPasswd", $App->GetPhrase('Create .htaccess/.htpasswd files'));
	$App->print("\n");
	$App->Textbox('PasswdFileName',         30, $pParams->{PasswdFileName},  '<b>' . $App->GetPhrase('Passwd FileName') . ':</b> ');
#	$App->print("\n");
	$App->print("     ");
	$App->Textbox('AuthName',               30, $pParams->{AuthName},        '<b>' . $App->GetPhrase('Auth Name')       . ':</b> ');
	$App->print("\n");
	$App->Textbox('AccountName',            30, $pParams->{AccountName},     '<b>' . $App->GetPhrase('Account Name')    . ':</b> ');
#	$App->print("\n");
	$App->print("     ");
	$App->PasswordTextbox('AccessPassword', 30, $pParams->{AccessPassword},  '<b>' . $App->GetPhrase('Password')        . ':</b> ');
	$App->print("\n");
	$App->PrintRawHTML("$Option\n");
	$App->EndForm(undef, undef, undef, undef);
}

sub CreateHTAccess
{
	my ($this, $App, $pParams, $pPrevFunc, $pPostFunc) = @_;
	return if(!$this->IsOpen($App, $pParams, 0));
	return if(!$App->HasPrivilege($CreateHTAccessPrivilege, 0));

my $Overwrite = 1;
	$App->ShowSubMenu($pParams, $pParams->{SubMenuFunction});

	$App->H2($App->GetPhrase('Create .htaccess/.htpasswd') . "\n");

	if($pParams->{PasswdFileName} eq '') {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: PasswdFileName must be specified.\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}

	if(length($pParams->{AccessPassword}) < 6) {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: AccessPassword must be longer than 6 characters.\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}

	$pParams->{Path} = Utils::URLDecode($pParams->{Path});
	my $PasswordDir  = Utils::MakePath($pParams->{BinFileRootDir}, 'Password', '/', 0);
	my $HTAccessPath = Utils::MakePath($pParams->{Path}, '.htaccess', '/', 0);
#	my $HTPasswdPath = Utils::MakePath($pParams->{Path}, '.htpasswd', '/', 0);
	my $HTPasswdPath = Utils::MakePath($PasswordDir,     $pParams->{PasswdFileName}, '/', 0);
$App->print("HTAccessPath: $HTAccessPath\n");
$App->print("HTPasswdPath: $HTPasswdPath\n");

	if(!$Overwrite and -e $HTAccessPath) {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: [$HTAccessPath] already exists.\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}
	if(!$Overwrite and -e $HTPasswdPath) {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: [$HTPasswdPath] already exists.\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}

	my $out = new JFile($HTAccessPath, 'w');
	if(!$out) {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: Can not write to [$HTAccessPath].\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}
#	$out->print("Options -Indexes\n");
	$out->print("\n");
	$out->print("AuthUserFile  $HTPasswdPath\n");
	$out->print("AuthGroupFile /dev/null\n");
	$out->print("AuthName $pParams->{AuthName}\n");
	$out->print("AuthType Basic\n");
	$out->print("require  valid-user\n");
	$out->print("\n");
	$out->print("<Files ~ \"^.(htpasswd|htaccess)\$\">\n");
	$out->print("  deny from all\n");
	$out->print("</Files>\n");
	$out->print("order deny,allow\n");
#order deny,allow
# deny from all
# allow from xxxxx.net
# allow from yyyyy.com
# allow from 123.456.789.012
# allow form 234.567.890.123 
	
	$out->Close();
	$App->H3("[$HTAccessPath] has been created.\n");

	my $out = new JFile($HTPasswdPath, 'w');
	if(!$out) {
		$App->H3("Error in ${ModuleName}::CreateHTPasswd: Can not write to [$HTPasswdPath].\n");
		$App->HR();
		$this->ShowPage($App, $pParams, undef, undef, 0);
		return;
	}
	my $EncPassword = Utils::CryptForHTPasswd($pParams->{AccessPassword});
	$out->print("$pParams->{AccountName}:$EncPassword\n");
	$out->Close();
	$App->H3("[$HTPasswdPath] has been created.\n");

	$App->HR();
	$this->ShowPage($App, $pParams, undef, undef, 0);
}

sub ShowChangePasswordPage
{
	my ($this, $App, $pParams) = @_;
	return if(!$this->IsOpen($App, $pParams, 0));
	return if(!$App->HasPrivilege($ChangePasswordPrivilege, 0));

	my $DownloadAccountKey = $this->{DownloadAccountKey};
	if($pParams->{$DownloadAccountKey} eq '') {
		$App->H2("Error in ${ModuleName}::ShowChangePasswordPage: Variable [$DownloadAccountKey] is not defined.\n");
		return;
	}

	my ($Option, $target) = $App->GetCGIParameters(
			{
#				Action      => "${ModuleName}::ChangePassword",
				Action      => ($pParams->{NextAction})? $pParams->{NextAction} : "${ModuleName}::ChangePassword",
				PrevAction  => $pParams->{Action},
				AccountName => $pParams->{$DownloadAccountKey},
			},
			[qw(+Action NextAction Path Key SubMenuFunction IsChildFrame FrameMode)],
			undef, undef, 1,
		);
	$pParams->{PasswdFileName} = '.htpasswd'   if($pParams->{PasswdFileName} eq '');
	$pParams->{AuthName}       = 'SecretFiles' if($pParams->{AuthName} eq '');
	$App->BeginForm($pParams, $pParams->{ScriptPath}, "POST", "_self", 0);
	$App->PrintFormButton("ChangePassword", $App->GetPhrase('Change password'));
	$App->print("     ");
#	$App->print("\n");
	$App->PrintRawHTML("<b>". $App->GetPhrase('Account') . ": </b> $pParams->{$DownloadAccountKey}\n");
	$App->print("  ");
#	$App->print("\n");
	$App->PasswordTextbox('AccountPassword', 30, $pParams->{AccountPassword},   '<b>' . $App->GetPhrase('Password') . ':</b> ');
	$App->PrintRawHTML("$Option\n");
	$App->EndForm(undef, undef, undef, undef);
}

sub ChangePassword
{
	my ($this, $App, $pParams) = @_;
	return if(!$this->IsOpen($App, $pParams, 0));
	return if(!$App->HasPrivilege($ChangePasswordPrivilege, 0));

	my $DownloadAccountKey       = $this->{DownloadAccountKey};
	my $DownloadAccountName      = $this->{DownloadAccountName};
	my $DownloadAccountPrivilege = $this->{DownloadAccountPrivilege};
	my $MinPasswordLength        = $this->{MinPasswordLength};

	if($pParams->{$DownloadAccountKey} eq '') {
		$App->H2("Error in ${ModuleName}::ChangePassword: Variable [$DownloadAccountKey] is not defined.\n");
		$App->HR();
		$this->ShowPage($App, $pParams);
		return;
	}
	if(length($pParams->{AccountPassword}) < $MinPasswordLength) {
		if($pParams->{AccountPassword} eq '') {
			$App->mlPrintRawHTML('en' => "Error: NULL Password is not allowed.\n",
					     'jp' => "Error: 空パスワードは使えません。\n");
		}
		else {
			$App->mlPrintRawHTML('en' => "Error: Password is too short (must be $MinPasswordLength characters or longer).\n",
					     'jp' => "Error: パスワードが短すぎます ($MinPasswordLength文字以上にしてください).\n");
		}
		$App->HR();
		$this->ShowPage($App, $pParams);
		return;
	}

	my $DownloadAccount = $pParams->{$DownloadAccountKey};
	my $DB = $App->DB();
	$pParams->{DBUsersTableName} = $pParams->{DBRegistersTableName} if($pParams->{DBUsersTableName} eq '');

	$DB->Search($pParams->{DBUsersTableName}, "EMail='$DownloadAccount' order by sn", "");
	my $nHit = $DB->nHit();
#$App->print("$nHit found for Account [$DownloadAccount].\n");
	my $Now = time();
	if($nHit > 0) {
		my @DBList = (
			Password   => $pParams->{AccountPassword},
			);
		if($pParams->{ProgramBaseName} =~ /Laboratory/) {
			@DBList = (
				@DBList,
				UpdateDate       => $Now,
				);
		}
		my $ret = $DB->UpdateData($pParams->{DBUsersTableName}, "EMail=\'$DownloadAccount\'", @DBList);
		if($ret) {
			$App->mlPrintRawHTML('en' => "Password for [$DownloadAccount] updated.<br>\n",
					     'jp' => "[$DownloadAccount]のパスワードを更新しました。<br>\n");
		}
		else {
			$App->mlPrintRawHTML('en' => "Errorr in ${ModuleName}::ChangePassword: Password for [$DownloadAccount] could not be updated.<br>\n",
					     'jp' => "Errorr in ${ModuleName}::ChangePassword: 不明な理由で[$DownloadAccount]のパスワードを更新できませんでした。<br>\n");
		}
	}
	else {
		my @DBList = (
			EMail            => $DownloadAccount,
			Name             => $DownloadAccountName,
			Password         => $pParams->{AccountPassword},
			Privilege        => $DownloadAccountPrivilege,
			);
		if($pParams->{ProgramBaseName} =~ /Laboratory/) {
			@DBList = (
				@DBList,
				Status => 'Active',
				UpdateDate       => $Now,
				RecordedDate     => $Now,
				);
		}
		my $ret = $DB->InsertData($pParams->{DBUsersTableName}, @DBList);
		if($ret) {
			$App->mlPrintRawHTML('en' => "Account [$DownloadAccount] has been created.<br>\n",
					     'jp' => "アカウント[$DownloadAccount]を作成しました。<br>\n");
#$App->print("[$DownloadAccountName][$pParams->{AccountPassword}][$DownloadAccountPrivilege][$DownloadAccount] has been created.<br>\n");
		}
		else {
			$App->mlPrintRawHTML('en' => "Errorr in ${ModuleName}::ChangePassword: Create Account [$DownloadAccount] failed "
						    ."(Table [$pParams->{DBUsersTableName}]).<br>\n",
					     'jp' => "Errorr in ${ModuleName}::ChangePassword: アカウント[$DownloadAccount]の作成に失敗しました "
					     	    ."(Table [$pParams->{DBUsersTableName}])。<br>\n");
		}
	}

	$App->HR();
	$this->ShowPage($App, $pParams);
}

sub DownloadFile {
	my ($this, $App, $pParams, $Path) = @_;
	$this->Download2($App, $pParams, $Path);
}

sub Download {
	my ($this, $App, $pParams, $Path) = @_;
	$this->Download2($App, $pParams, $Path);
}

sub Download2 {
	my ($this, $App, $pParams, $Path) = @_;
	$App->SendNotificationForPrivilegedAccessMail($App, $pParams);
	return if(!$this->IsOpen($App, $pParams, 0));

	$Path = $pParams->{Path} if(!defined $Path and defined $pParams->{Path});
	$Path = Utils::URLDecode($Path);
	$Path = Utils::DelQuote($Path);
	$Path =~ s/\\/\//g;
Jcode::convert(\$Path, $pParams->{FileSystemCharCode});
	if(!-e $Path) {
		$Path = Utils::MakePath($this->{RootPath}, $Path, '/', 0);
	}
#$App->print("p[$Path]\n");
	$this->URLReverseSubstitution($App, $pParams, $Path);

	my $CheckFileName = $Path;
	$CheckFileName =~ s/^file:\/\///i;
	my ($IsPermitted, $pd, $bf, $bd, $ss, $st) = $this->IsPermitted($App, $pParams, $CheckFileName, $this->{pPermittedDirs});
	if(!$IsPermitted) {
		$App->H2("Error: Browsing $Path is not permitted.");
		return;
	}

	my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($Path);
#	my $DownloadPath = Utils::URLEncode($filename);
	my $DownloadPath = $filename;
Jcode::convert(\$DownloadPath, $DownloadFileCharCode, $pParams->{FileSystemCharCode});
	$App->MyCGIApplication::DownloadFile($CheckFileName, $DownloadPath);
}

sub IsPermitted
{
	my ($this, $App, $pParams, $Path) = @_;

	my @PermittedDirs = @{$this->{pPermittedDirs}} if(defined $this->{pPermittedDirs});
	my @BrowseFiles   = @{$this->{pBrowseFiles}}   if(defined $this->{pBrowseFiles});
	my @BrowseDirs    = @{$this->{pBrowseDirs}}    if(defined $this->{pBrowseDirs});
	my @DenyFiles     = @{$this->{pDenyFiles}}     if(defined $this->{pDenyFiles});
	my @DenyDirs      = @{$this->{pDenyDirs}}      if(defined $this->{pDenyDirs});
	my @SubstitutionSource = @{$this->{pSubstitutionSource}} if(defined $this->{pSubstitutionSource});
	my @SubstitutionTarget = @{$this->{pSubstitutionTarget}} if(defined $this->{pSubstitutionTarget});

	my $IsPermitted = 0;
	my ($pd, $bf, $bd, $ss, $st, $df, $dd);
	for(my $i = 0 ; $i < @PermittedDirs ; $i++) {
#print "PermDir[$i] (this=$this): [$Path][$PermittedDirs[$i]]<br>\n";
		$pd = $PermittedDirs[$i];
		$pd =~ s/\\/\//g;
#		$pd =~ s/\//\\\//g;
#$App->print("$i: $Path [$pd]\n");
		if($Path =~ /$pd/i) {
#$App->print("  passed\n");
			$IsPermitted = 1;
			$bf = $BrowseFiles[$i];
			$bd = $BrowseDirs[$i];
			$df = $DenyFiles[$i];
			$dd = $DenyDirs[$i];
			$ss = $SubstitutionSource[$i];
			$st = $SubstitutionTarget[$i];
			last;
		}
	}
#$App->print("Ret=$IsPermitted: [$Path] $pd, $bd, $ss, $st\n");
	my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($Path);
	if(-d $Path) {
#$App->print("[$Path] is directory\n");
		$lastdir = $filename;
		$directory = $Path;
		$filename = $filebody = $ext = '';
	}
#$App->print("f=[$filename] [$bf][$bd][$df][$dd]\n");
	if($filename ne '' and $bf ne '' and $filename !~ /$bf/i) {
		$IsPermitted = 0;
	}
	if($filename ne '' and $df ne '' and $filename =~ /$df/i) {
		$IsPermitted = 0;
	}
	if($bd ne '' and $Path !~ /$bd/i) {
		$IsPermitted = 0;
	}
#$App->print("Path: $Path, dd=$dd\n");
	if($dd ne '' and $Path =~ /$dd/i) {
		$IsPermitted = 0;
	}

	return ($IsPermitted, $pd, $bf, $bd, $ss, $st);
}

sub URLSubstitution
{
	my ($this, $App, $pParams, $url) = @_;

	my @URLSubstitutionList = @{$this->{pURLSubstitutionList}} if(defined $this->{pURLSubstitutionList});
	for(my $i = 0 ; $i < @URLSubstitutionList ; $i += 2) {
		my $src    = @URLSubstitutionList[$i];
		my $target = @URLSubstitutionList[$i+1];
		$url =~ s/$src/$target/gi;
	}
	return $url;
}

sub URLReverseSubstitution
{
	my ($this, $App, $pParams, $url) = @_;

	my @URLReverseSubstitutionList = @{$this->{pURLReverseSubstitutionList}} if(defined $this->{pURLReverseSubstitutionList});
	for(my $i = 0 ; $i < @URLReverseSubstitutionList ; $i += 2) {
		my $src    = @URLReverseSubstitutionList[$i];
		my $target = @URLReverseSubstitutionList[$i+1];
		$url =~ s/$src/$target/gi;
	}
	return $url;
}
1;
