From 16755c17628b28a58d75663d7541036344826961 Mon Sep 17 00:00:00 2001 From: cbarratt Date: Fri, 6 Jul 2007 06:43:44 +0000 Subject: [PATCH] * Added Simplified Chinese CGI translation from Youlin Feng. * Added -l and -L options to BackupPC_tarCreate so that provide a file list (without creating the archive). Requested by Dirk. * Fixed single-restore file name charsets for IE, reported by Francis Lessard. * Fixed makeDist so that the --config-dir option to configure.pl works correctly. Reported by Randy Barlow, Tony Shadwick and others. * Applied patch from Holger Parplies that fixes cleanup of early abort in BackupPC_dump. * Changed BackupPC_sendEmail so that summary admin email doesn't include errors from hosts that have $Conf{BackupsDisable} set. Reported by James Kyle. Also, per-user email is now disabled when $Conf{BackupsDisable} is set. * $Conf{IncrLevels} is now defaulted if it is not defined. --- ChangeLog | 22 + bin/BackupPC | 3 +- bin/BackupPC_archiveStart | 120 +++ bin/BackupPC_dump | 5 +- bin/BackupPC_sendEmail | 181 ++-- bin/BackupPC_tarCreate | 74 +- conf/config.pl | 4 +- lib/BackupPC/Attrib.pm | 5 + lib/BackupPC/CGI/RestoreFile.pm | 16 +- lib/BackupPC/Config/Meta.pm | 2 +- lib/BackupPC/Lang/zh_CN.pm | 1437 ++++++++++++++++++++++++++++++ lib/BackupPC/Lib.pm | 22 +- lib/BackupPC/Storage.pm | 2 +- lib/BackupPC/Storage/Text.pm | 19 + lib/BackupPC/View.pm | 8 +- lib/BackupPC/Xfer/RsyncFileIO.pm | 41 +- makeDist | 2 + 17 files changed, 1828 insertions(+), 135 deletions(-) create mode 100755 bin/BackupPC_archiveStart create mode 100644 lib/BackupPC/Lang/zh_CN.pm diff --git a/ChangeLog b/ChangeLog index ffcc7ca..f184c6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,15 +34,37 @@ * Added sorting by column feature to host summary table in CGI interface. Implemented by Jeremy Tietsort. +* Added Simplified Chinese CGI translation from Youlin Feng. + * Added FreeBSD init.d file provided by Gabriel Rossetti. +* Added -l and -L options to BackupPC_tarCreate so that + provide a file list (without creating the archive). + Requested by Dirk. + +* Fixed single-restore file name charsets for IE, reported by + Francis Lessard. + +* Fixed makeDist so that the --config-dir option to configure.pl + works correctly. Reported by Randy Barlow, Tony Shadwick and others. + +* Applied patch from Holger Parplies that fixes cleanup of early abort + in BackupPC_dump. + * Applied small patch from Sergey to lib/BackupPC/Xfer/Tar.pm that makes it ignore "socket ignored" error on incrementals. * Applied small patch from Sergey to bin/BackupPC_archiveHost. +* Changed BackupPC_sendEmail so that summary admin email doesn't + include errors from hosts that have $Conf{BackupsDisable} set. + Reported by James Kyle. Also, per-user email is now disabled + when $Conf{BackupsDisable} is set. + * Added RsyncdUserName to the config editor. Reported by Vicent Roca Daniel. +* $Conf{IncrLevels} is now defaulted if it is not defined. + * configure.pl clears $Conf{ParPath} if it doesn't point to a valid executable. diff --git a/bin/BackupPC b/bin/BackupPC index c4520b1..2053aa5 100755 --- a/bin/BackupPC +++ b/bin/BackupPC @@ -59,6 +59,7 @@ use vars qw(%Status %Info $Hosts); use lib "/usr/local/BackupPC/lib"; use BackupPC::Lib; use BackupPC::FileZIO; +use Encode; use File::Path; use Data::Dumper; @@ -1266,7 +1267,7 @@ sub Main_Check_Client_Messages last; } $Clients{$client}{mesgCnt}++; - $cmd = $2; + $cmd = decode_utf8($2); if ( $cmd =~ /^stop (\S+)\s+(\S+)\s+(\S*)/ ) { $host = $1; my $user = $2; diff --git a/bin/BackupPC_archiveStart b/bin/BackupPC_archiveStart new file mode 100755 index 0000000..54ec76a --- /dev/null +++ b/bin/BackupPC_archiveStart @@ -0,0 +1,120 @@ +#!/bin/perl +#============================================================= -*-perl-*- +# +# BackupPC_archiveStart: start an archive request from the +# command line. +# +# DESCRIPTION +# +# Usage: BackupPC_archiveStart archiveHost userName hosts... +# +# Initiates an archive request on archive host archiveHost +# for the listed hosts. The latest backup for each host is +# archived. The userName is name of the requesting user, +# which appears in the log files. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2007 Craig Barratt +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#======================================================================== +# +# Version 3.0.0, released 28 Jan 2007. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +use strict; +no utf8; +use lib "/usr/local/BackupPC/lib"; +use Getopt::Std; +use BackupPC::Lib; + +die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); + +my %opts; + +# no options currently +if ( !getopts("", \%opts) || @ARGV < 3 ) { + print STDERR <HostInfoRead(); +my $ArchiveHost = $ARGV[0]; +my $UserName = $ARGV[1]; +my $TopDir = $bpc->{Conf}{TopDir}; + +if ( !defined($Hosts->{$ArchiveHost}) ) { + print(STDERR "$0: archive host $ArchiveHost doesn't exist... quitting\n"); + exit(1); +} +$bpc->ConfigRead($ArchiveHost); + +my(@HostList, @BackupList); +for ( my $i = 2 ; $i < @ARGV ; $i++ ) { + my $host = $ARGV[$i]; + if ( !defined($Hosts->{$host}) ) { + print(STDERR "$0: host $host doesn't exist... quitting\n"); + exit(1); + } + my @backups = $bpc->BackupInfoRead($host); + if ( !@backups ) { + print(STDERR "$0: host $host doesn't have any backups... quitting\n"); + exit(1); + } + push(@HostList, $host); + push(@BackupList, $backups[$#backups]{num}); +} + +my $ReqFileName; +for ( my $i = 0 ; ; $i++ ) { + $ReqFileName="archiveReq.$$.$i"; + last if ( !-f "$TopDir/pc/$ArchiveHost/$ReqFileName" ); +} +my %ArchiveReq = ( + archiveloc => $bpc->{Conf}{ArchiveDest}, + archtype => 0, + compression => $bpc->{Conf}{CatPath}, + compext => '.raw', + parfile => $bpc->{Conf}{ArchivePar}, + splitsize => '0000000', + host => $ArchiveHost, + HostList => \@HostList, + BackupList => \@BackupList, + user => $UserName, + reqTime => time, +); +my $archive = Data::Dumper->new([\%ArchiveReq], [qw(*ArchiveReq)]); +$archive->Indent(1); +if ( !open(REQ, ">", "$TopDir/pc/$ArchiveHost/$ReqFileName") ) { + print(STDERR "$0: can't open/write request file $TopDir/pc/$ArchiveHost/$ReqFileName... quitting\n"); + exit(1); +} +binmode(REQ); +print REQ $archive->Dump; +close(REQ); +$bpc->ServerConnect($bpc->{Conf}{ServerHost}, $bpc->{Conf}{ServerPort}); +my $reply = $bpc->ServerMesg("archive $UserName $ArchiveHost $ReqFileName"); +$bpc->ServerDisconnect(); +print("Sent archive request, reply: $reply\n"); +exit(0); diff --git a/bin/BackupPC_dump b/bin/BackupPC_dump index 28f3ef7..d0925f0 100755 --- a/bin/BackupPC_dump +++ b/bin/BackupPC_dump @@ -1012,9 +1012,9 @@ sub catch_signal "Aborting backup up after signal $sigName\n"); # - # Tell xfer to abort + # Tell xfer to abort, but only if we actually started one # - $xfer->abort($reason); + $xfer->abort($reason) if ( defined($xfer) ); # # Send ALRMs to BackupPC_tarExtract if we are using it @@ -1417,6 +1417,7 @@ sub BackupSave $Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ $Backups[$i]{xferMethod} = $Conf{XferMethod}; $Backups[$i]{charset} = $Conf{ClientCharset}; + $Backups[$i]{version} = $bpc->Version(); # # Save the main backups file # diff --git a/bin/BackupPC_sendEmail b/bin/BackupPC_sendEmail index 802b8f0..d826893 100755 --- a/bin/BackupPC_sendEmail +++ b/bin/BackupPC_sendEmail @@ -120,99 +120,37 @@ EOF exit(0); } -########################################################################### -# Generate sysadmin warning messages -########################################################################### -my $mesg = ""; - -my @badHosts = (); -foreach my $host ( sort(keys(%Status)) ) { - next if ( ($Status{$host}{reason} ne "Reason_backup_failed" - && $Status{$host}{reason} ne "Reason_restore_failed") - || $Status{$host}{error} =~ /^lost network connection to host/ ); - push(@badHosts, "$host ($Status{$host}{error})"); -} -if ( @badHosts ) { - my $badHosts = join("\n - ", sort(@badHosts)); - $mesg .= < 0 ) { - my $n = $Info{DUDailySkipHostCntPrev}; - my $m = $Conf{DfMaxUsagePct}; - $mesg .= <new("$TopDir/pc") or die("Can't read $TopDir/pc: $!"); -my @oldDirs = (); -my @files = $d->read; -$d->close; -foreach my $host ( @files ) { - next if ( $host =~ /^\./ || defined($Status{$host}) ); - push(@oldDirs, "$TopDir/pc/$host"); -} -if ( @oldDirs ) { - my $oldDirs = join("\n - ", sort(@oldDirs)); - $mesg .= <HostInfoRead(); +my @AdminBadHosts = (); foreach my $host ( sort(keys(%Status)) ) { - next if ( $Hosts->{$host}{user} eq "" ); # # read any per-PC config settings (allowing per-PC email settings) # $bpc->ConfigRead($host); %Conf = $bpc->Conf(); my $user = $Hosts->{$host}{user}; + + # + # Accumulate host errors for the admin email below + # + if ( ($Status{$host}{reason} eq "Reason_backup_failed" + || $Status{$host}{reason} eq "Reason_restore_failed") + && $Status{$host}{error} !~ /^lost network connection to host/ + && !$Conf{BackupsDisable} + ) { + push(@AdminBadHosts, "$host ($Status{$host}{error})"); + } + next if ( time - $UserEmailInfo{$user}{lastTime} - < $Conf{EMailNotifyMinDays} * 24*3600 ); - next if ($Conf{XferMethod} eq "archive" ); + < $Conf{EMailNotifyMinDays} * 24*3600 + || $Conf{XferMethod} eq "archive" + || $Conf{BackupsDisable} + || $Hosts->{$host}{user} eq "" + ); my @Backups = $bpc->BackupInfoRead($host); my $numBackups = @Backups; if ( $numBackups == 0 ) { @@ -326,6 +264,80 @@ foreach my $host ( sort(keys(%Status)) ) { }) if ( !defined($Jobs{$host}) ); } } + +########################################################################### +# Generate sysadmin warning message +########################################################################### +my $adminMesg = ""; + +if ( @AdminBadHosts ) { + my $badHosts = join("\n - ", sort(@AdminBadHosts)); + $adminMesg .= < 0 ) { + my $n = $Info{DUDailySkipHostCntPrev}; + my $m = $Conf{DfMaxUsagePct}; + $adminMesg .= <new("$TopDir/pc") or die("Can't read $TopDir/pc: $!"); +my @oldDirs = (); +my @files = $d->read; +$d->close; +foreach my $host ( @files ) { + next if ( $host =~ /^\./ || defined($Status{$host}) ); + push(@oldDirs, "$TopDir/pc/$host"); +} +if ( @oldDirs ) { + my $oldDirs = join("\n - ", sort(@oldDirs)); + $adminMesg .= <Dump( @@ -337,6 +349,7 @@ if ( !$opts{t} ) { close(HOST); } } +exit(0); sub user2name { @@ -386,7 +399,13 @@ sub SendMail printf("Can't run sendmail ($Conf{SendmailPath}): $!\n"); return; } - binmode(MAIL, ":utf8") if ( $utf8 ); + if ( $utf8 ) { + binmode(MAIL, ":utf8"); + if ($mesg =~ /^Subject: (.*)$/m) { + my $new_subj = encode('MIME-Header', $1); + $mesg =~ s/^Subject: .*$/Subject: $new_subj/m; + } + } print MAIL $mesg; close(MAIL); } diff --git a/bin/BackupPC_tarCreate b/bin/BackupPC_tarCreate index 500bd8b..5548672 100755 --- a/bin/BackupPC_tarCreate +++ b/bin/BackupPC_tarCreate @@ -25,6 +25,8 @@ # -w writeBufSz write buffer size (default 1MB) # -e charset charset for encoding file names (default: value of # $Conf{ClientCharset} when backup was done) +# -l just print a file listing; don't generate an archive +# -L just print a detailed file listing; don't generate an archive # # The -h, -n and -s options specify which dump is used to generate # the tar archive. The -r and -p options can be used to relocate @@ -74,7 +76,7 @@ die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my %opts; -if ( !getopts("te:h:n:p:r:s:b:w:", \%opts) || @ARGV < 1 ) { +if ( !getopts("Llte:h:n:p:r:s:b:w:", \%opts) || @ARGV < 1 ) { print STDERR <{linkname}, "utf8", $Charset); } + if ( $opts{l} ) { + print($hdr->{name} . "\n"); + return; + } elsif ( $opts{L} ) { + my $owner = "$hdr->{uid}/$hdr->{gid}"; + + my $name = $hdr->{name}; + + if ( $hdr->{type} == BPC_FTYPE_SYMLINK + || $hdr->{type} == BPC_FTYPE_HARDLINK ) { + $name .= " -> $hdr->{linkname}"; + } + $name =~ s/\n/\\n/g; + + printf("%6o %9s %11.0f %s\n", + $hdr->{mode}, + $owner, + $hdr->{size}, + $name); + return; + } + # # Handle long link names (symbolic links) # @@ -432,6 +456,7 @@ sub TarWriteFile TarWriteFileInfo($fh, $hdr); $DirCnt++; } elsif ( $hdr->{type} == BPC_FTYPE_FILE ) { + my($data, $size); # # Regular file: write the header and file # @@ -442,31 +467,34 @@ sub TarWriteFile return; } TarWriteFileInfo($fh, $hdr); - my($data, $size); - while ( $f->read(\$data, $BufSize) > 0 ) { - if ( $size + length($data) > $hdr->{size} ) { - print(STDERR "Error: truncating $hdr->{fullPath} to" - . " $hdr->{size} bytes\n"); - $data = substr($data, 0, $hdr->{size} - $size); - $ErrorCnt++; - } - TarWrite($fh, \$data); - $size += length($data); - } - $f->close; - if ( $size != $hdr->{size} ) { - print(STDERR "Error: padding $hdr->{fullPath} to $hdr->{size}" - . " bytes from $size bytes\n"); - $ErrorCnt++; - while ( $size < $hdr->{size} ) { - my $len = $hdr->{size} - $size; - $len = $BufSize if ( $len > $BufSize ); - $data = "\0" x $len; + if ( $opts{l} || $opts{L} ) { + $size = $hdr->{size}; + } else { + while ( $f->read(\$data, $BufSize) > 0 ) { + if ( $size + length($data) > $hdr->{size} ) { + print(STDERR "Error: truncating $hdr->{fullPath} to" + . " $hdr->{size} bytes\n"); + $data = substr($data, 0, $hdr->{size} - $size); + $ErrorCnt++; + } TarWrite($fh, \$data); - $size += $len; + $size += length($data); + } + $f->close; + if ( $size != $hdr->{size} ) { + print(STDERR "Error: padding $hdr->{fullPath} to $hdr->{size}" + . " bytes from $size bytes\n"); + $ErrorCnt++; + while ( $size < $hdr->{size} ) { + my $len = $hdr->{size} - $size; + $len = $BufSize if ( $len > $BufSize ); + $data = "\0" x $len; + TarWrite($fh, \$data); + $size += $len; + } } + TarWritePad($fh, $size); } - TarWritePad($fh, $size); $FileCnt++; $ByteCnt += $size; } elsif ( $hdr->{type} == BPC_FTYPE_HARDLINK ) { diff --git a/conf/config.pl b/conf/config.pl index eac455e..fd78dfc 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -1862,8 +1862,8 @@ $Conf{CgiURL} = undef; # # Language to use. See lib/BackupPC/Lang for the list of supported # languages, which include English (en), French (fr), Spanish (es), -# German (de), Italian (it), Dutch (nl) and Portuguese Brazillian -# (pt_br). +# German (de), Italian (it), Dutch (nl), Portuguese Brazillian +# (pt_br) and Chinese (zh_CH). # # Currently the Language setting applies to the CGI interface and email # messages sent to users. Log files and other text are still in English. diff --git a/lib/BackupPC/Attrib.pm b/lib/BackupPC/Attrib.pm index 4618c69..2a76244 100644 --- a/lib/BackupPC/Attrib.pm +++ b/lib/BackupPC/Attrib.pm @@ -43,6 +43,7 @@ use strict; use Carp; use File::Path; use BackupPC::FileZIO; +use Encode; require Exporter; use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); @@ -198,6 +199,8 @@ sub read my($data); $file = $a->fileName($dir, $file); + from_to($file, "utf8", $a->{charsetLegacy}) + if ( $a->{charsetLegacy} ne "" ); my $fd = BackupPC::FileZIO->open($file, 0, $a->{compress}); if ( !$fd ) { $a->{_errStr} = "Can't open $file"; @@ -240,6 +243,8 @@ sub read } } (my $fileName, $data) = unpack("a$len a*", $data); + from_to($fileName, $a->{charsetLegacy}, "utf8") + if ( $a->{charsetLegacy} ne "" ); my $nFldsW = @FldsUnixW; my $nFldsN = @FldsUnixN; if ( length($data) < 5 * $nFldsW + 4 * $nFldsN ) { diff --git a/lib/BackupPC/CGI/RestoreFile.pm b/lib/BackupPC/CGI/RestoreFile.pm index e5b316a..2a726d2 100644 --- a/lib/BackupPC/CGI/RestoreFile.pm +++ b/lib/BackupPC/CGI/RestoreFile.pm @@ -37,12 +37,11 @@ package BackupPC::CGI::RestoreFile; use strict; -use Encode; use BackupPC::CGI::Lib qw(:all); use BackupPC::FileZIO; use BackupPC::Attrib qw(:all); use BackupPC::View; - +use Encode qw/from_to/; sub action { @@ -144,6 +143,8 @@ sub restoreFile if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files2}}")); } + $bpc->ConfigRead($host); + %Conf = $bpc->Conf(); ServerConnect(); ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); @@ -177,8 +178,19 @@ sub restoreFile || "application/octet-stream"; my $fileName = $1 if ( $dir =~ /.*\/(.*)/ ); $fileName =~ s/"/\\"/g; + print "Content-Type: $contentType\n"; print "Content-Transfer-Encoding: binary\n"; + + if ( $ENV{HTTP_USER_AGENT} =~ /\bmsie\b/i ) { + # + # Convert to cp1252 for MS IE. TODO: find a way to get IE + # to accept UTF8 encoding. Firefox accepts inline encoding + # using the "=?UTF-8?B?base64?=" format, but IE doesn't. + # + from_to($fileName, "utf8", "cp1252") + if ( $Conf{ClientCharset} ne "" ); + } print "Content-Disposition: attachment; filename=\"$fileName\"\n\n"; while ( $f->read(\$data, 1024 * 1024) > 0 ) { print STDOUT $data; diff --git a/lib/BackupPC/Config/Meta.pm b/lib/BackupPC/Config/Meta.pm index 1ad7d85..f21df2d 100644 --- a/lib/BackupPC/Config/Meta.pm +++ b/lib/BackupPC/Config/Meta.pm @@ -312,7 +312,7 @@ use vars qw(%ConfigMeta); CgiURL => "string", Language => { type => "select", - values => [qw(de en es fr it nl pt_br)], + values => [qw(de en es fr it nl pt_br zh_CN)], }, CgiUserHomePageCheck => "string", CgiUserUrlCreate => "string", diff --git a/lib/BackupPC/Lang/zh_CN.pm b/lib/BackupPC/Lang/zh_CN.pm new file mode 100644 index 0000000..645711a --- /dev/null +++ b/lib/BackupPC/Lang/zh_CN.pm @@ -0,0 +1,1437 @@ +#!/usr/bin/perl + +#my %lang; +#use strict; +use utf8; + +# -------------------------------- + +$Lang{Start_Archive} = "开始备档"; +$Lang{Stop_Dequeue_Archive} = "中止/取消备档"; +$Lang{Start_Full_Backup} = "开始完全备份"; +$Lang{Start_Incr_Backup} = "开始增量备份"; +$Lang{Stop_Dequeue_Backup} = "中止/取消备份"; +$Lang{Restore} = "恢复"; + +$Lang{Type_full} = "完全"; +$Lang{Type_incr} = "增量"; + +# ----- + +$Lang{Only_privileged_users_can_view_admin_options} = "只有特权用户可以查看管理选项。"; +$Lang{H_Admin_Options} = "BackupPC 服务器:管理选项"; +$Lang{Admin_Options} = "管理选项"; +$Lang{Admin_Options_Page} = < +\${h2("服务器控制")} +
+ + +
更新服务器配置: +
+
+ +EOF +$Lang{Unable_to_connect_to_BackupPC_server} = "无法连接到 BackupPC 服务器", + "CGI 脚本程序 (\$MyURL) 无法连接到 BackupPC 服务器 \$Conf{ServerHost} 端口 \$Conf{ServerPort}。错误信息:\$err。", + "可能 BackupPC 服务器没有运行,或者服务器配置不正确。请通知网络系统管理员。"; +$Lang{Admin_Start_Server} = < +BackupPC 服务器 \$Conf{ServerHost} 端口 \$Conf{ServerPort} +此刻没有运行(可能刚被停止,或者还没被启动)。
+你想现在启动它吗? + + + +EOF + +# ----- + +$Lang{H_BackupPC_Server_Status} = "BackupPC 服务器状态"; + +$Lang{BackupPC_Server_Status_General_Info}= < +
  • 服务器进程号是 \$Info{pid},运行在主机 \$Conf{ServerHost} 上, + 版本号 \$Info{Version},开始运行于 \$serverStartTime。 +
  • 此状态报告生成于 \$now。 +
  • 服务器配置最近一次加载于 \$configLoadTime。 +
  • 服务器任务队列下次启动时间是 \$nextWakeupTime。 +
  • 其它信息: +
      +
    • \$numBgQueue 个自上次遗留备份请求, +
    • \$numUserQueue 个待处理用户备份请求, +
    • \$numCmdQueue 个待处理命令请求, + \$poolInfo +
    • 备份池文件系统磁盘空间占用率是 \$Info{DUlastValue}% + (统计于 \$DUlastTime),今天的最大占用率是 \$Info{DUDailyMax}%(统计于 \$DUmaxTime), + 昨天的最大占用率是 \$Info{DUDailyMaxPrev}%。 +
    + +EOF + +$Lang{BackupPC_Server_Status} = < +\$generalInfo + +\${h2("正在运行的任务")} +

    + + + + + + + + + +\$jobStr +
    客户机 类型 用户 开始时间 命令 进程号 传输进程号
    +

    + +\${h2("需要关注的错误")} +

    + + + + + + + + +\$statusStr +
    客户机 类型 用户 最后一次尝试 详情 错误时间 最后一次错误( PING 失败除外)
    +EOF + +# -------------------------------- +$Lang{BackupPC__Server_Summary} = "BackupPC: 客户机报告"; +$Lang{BackupPC__Archive} = "BackupPC: 备档"; +$Lang{BackupPC_Summary} = < +此状态报告生成于 \$now。 +

    + +\${h2("已成功完成备份的客户机")} +

    +有 \$hostCntGood 台客户机已完成备份,总数是: +

      +
    • \$fullTot 个完全备份,总大小是 \${fullSizeTot}GB + (被压缩前值), +
    • \$incrTot 个增量备份,总大小是 \${incrSizeTot}GB + (被压缩前值)。 +
    +

    + + + + + + + + + + + + +\$strGood +
    客户机 用户 完全备份个数 最后一次完全备份 (天前) 完全备份大小 (GB) 完全备份速度 (MB/s) 增量备份个数 最后一次增量备份 (天前) 最后一次备份 (天前) 当前状态 最后一次备份结果
    +

    +\${h2("未备份过的客户机")} +

    +有 \$hostCntNone 台客户机从未被备份过。 +

    + + + + + + + + + + + + +\$strNone +
    客户机 用户 完全备份个数 最后一次完全备份 (天前) 完全备份大小 (GB) 完全备份速度 (MB/s) 增量备份个数 最后一次增量备份 (天前) 最后一次备份 (天前) 当前状态 最后一次备份结果
    +EOF + +$Lang{BackupPC_Archive} = < + + + +一共有 \$hostCntGood 台客户机已经被备份,总备份大小为 \${fullSizeTot}GB +

    +

    + + + + + + + + +\$strGood +\$checkAllHosts +
    客户机 用户 备份大小
    +
    +

    + +EOF + +$Lang{BackupPC_Archive2} = < +\$HostListStr + +

    +\$hiddenStr + + + + + +\$paramStr + + + + +
    +EOF + +$Lang{BackupPC_Archive2_location} = < + 备档目的地/外设 + + +EOF + +$Lang{BackupPC_Archive2_compression} = < + 压缩 + + 无
    + gzip
    + bzip2 + + +EOF + +$Lang{BackupPC_Archive2_parity} = < + 奇偶校验数据比例 (0 = 不启用,5 = 典型设置) + + +EOF + +$Lang{BackupPC_Archive2_split} = < + 将输出分开为 + 兆字节 + +EOF + +# ----------------------------------- +$Lang{Pool_Stat} = <备份服务器文件池大小是 \${poolSize}GB 包含 \$info->{"\${name}FileCnt"} 个文件 + 和 \$info->{"\${name}DirCnt"} 个文件夹/目录(截至 \$poolTime)。文件池大小基本就是所有备份数据占用的实际磁盘空间。 +
  • 服务器文件池散列操作(Hashing)发现 \$info->{"\${name}FileCntRep"} + 个文件具有重复散列值,其中 \$info->{"\${name}FileRepMax"} 个文件具有相同散列值。相同散列值并不意味着相同文件。散列操作被用来节省相同文件所占用的磁盘空间。 +
  • 每日例行清理过期数据操作删除了 \$info->{"\${name}FileCntRm"} 个文件共 + \${poolRmSize}GB (操作于 \$poolTime )。 +EOF + +# -------------------------------- +$Lang{BackupPC__Backup_Requested_on__host} = "BackupPC: 客户机 \$host 有备份请求"; +# -------------------------------- +$Lang{REPLY_FROM_SERVER} = < +服务器答复是:\$reply +

    +返回 \$host 主页。 +EOF +# -------------------------------- +$Lang{BackupPC__Start_Backup_Confirm_on__host} = "BackupPC: 客户机 \$host 开始备份确认"; +# -------------------------------- +$Lang{Are_you_sure_start} = < +你即将在客户机 \$host 上开始 \$type 备份。 + +

    + + + + +你能确定吗? + + +
    +EOF +# -------------------------------- +$Lang{BackupPC__Stop_Backup_Confirm_on__host} = "BackupPC: 客户机 \$host 停止备份确认"; +# -------------------------------- +$Lang{Are_you_sure_stop} = < +你即将在客户机 \$host 上停止/取消备份操作; + +
    + + + +如果确定取消备份操作,请从现在起 + 小时内不要再启动另一备份操作。 +

    +你能确定吗? + + +

    + +EOF +# -------------------------------- +$Lang{Only_privileged_users_can_view_queues_} = "只有特权用户可以查看任务请求队列。"; +# -------------------------------- +$Lang{Only_privileged_users_can_archive} = "只有特权用户可以执行备档操作。"; +# -------------------------------- +$Lang{BackupPC__Queue_Summary} = "BackupPC: 队列报告"; +# -------------------------------- +$Lang{Backup_Queue_Summary} = <
    +\${h2("用户队列报告")} +

    +下列用户请求排在队列中: +

    + + + + +\$strUser +
    客户机 请求时间 用户
    +

    + +\${h2("后台请求队列报告")} +

    +下列后台请求排在队列中: +

    + + + + +\$strBg +
    客户机 请求时间 用户
    +

    +\${h2("命令队列报告")} +

    +下列命令请求排在队列中: +

    + + + + + +\$strCmd +
    客户机 请求时间 用户 命令
    +EOF + +# -------------------------------- +$Lang{Backup_PC__Log_File__file} = "BackupPC: 日志文件 \$file"; +$Lang{Log_File__file__comment} = < +EOF +# -------------------------------- +$Lang{Contents_of_log_file} = <\$file, 修改时间 \$mtimeStr \$comment +EOF + +# -------------------------------- +$Lang{skipped__skipped_lines} = "[ 略过 \$skipped 行 ]\n"; +# -------------------------------- +$Lang{_pre___Can_t_open_log_file__file} = "
    \n无法打开日志文件 \$file\n";
    +
    +# --------------------------------
    +$Lang{BackupPC__Log_File_History} = "BackupPC: 日志文件历史";
    +$Lang{Log_File_History__hdr} = <
    +
    +
    +    
    +    
    +\$str
    +
    文件 大小 修改时间
    +EOF + +# ------------------------------- +$Lang{Recent_Email_Summary} = < + + + + + +\$str +
    收信人 客户机 时间 标题
    +EOF + + +# ------------------------------ +$Lang{Browse_backup__num_for__host} = "BackupPC: 浏览客户机 \$host 备份序列号 \$num"; + +# ------------------------------ +$Lang{Restore_Options_for__host} = "BackupPC: 客户机 \$host 恢复选项"; +$Lang{Restore_Options_for__host2} = < +你从备份序列 #\$num,卷 \$share 中选择了以下文件/目录: +
      +\$fileListStr +
    +

    +你有三种选择来恢复这些文件/目录。 +请从下列三种方法中选择其一。 +

    +\${h2("方法 1:直接恢复")} +

    +EOF + +$Lang{Restore_Options_for__host_Option1} = <

    +警告: 客户机上现存的文件,如果和被恢复的文件具有相同文件名并且位于相同路径,其内容将会被替换! +

    +
    + + + +\$hiddenStr + + + + + + + + + + + + + +
    恢复到客户机 + + +
    恢复到卷
    恢复到此目录中
    (位于上述卷下)
    +
    +EOF + +$Lang{Restore_Options_for__host_Option1_disabled} = < +\${h2("方法 2:下载 Zip 备档")} +

    +你可以将所有你选择的文件和目录下载进一个 Zip 备档。然后再用一个本地应用, +例如 WinZip,来浏览或提取其中的任何文件。 +

    +警告: 取决于你选择的文件/目录,此备档可能会占用很大存储空间。 +可能需要若干分钟或更长时间来生成和传输此备档,并且还需要足够大的本地磁盘空间。 +

    +
    + + + +\$hiddenStr + + 备档中所有文件具有相对路径,在 \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} 目录内 +(否则备档中文件具有完整路径)。 +
    +选择压缩比(0=不压缩,1=最低但速度快,...,9=最高但速度慢) + +
    + +
    +EOF + +# ------------------------------ + +$Lang{Option_2__Download_Zip_archive2} = < +\${h2("方法 2:下载 Zip 备档")} +

    +因服务器没有安装 Perl 组件 Archive::Zip,Zip 备档无法被生成。 +请联系系统管理员安装 Archive::Zip,下载地址 +www.cpan.org。 +

    +EOF + + +# ------------------------------ +$Lang{Option_3__Download_Zip_archive} = < +你可以将所有你选择的文件和目录下载进一个 Tar 备档。然后再用一个本地应用, +例如 tar 或 WinZip,来浏览或提取其中的任何文件。 +

    +警告: 取决于你选择的文件/目录,此备档可能会占用很大存储空间。 +可能需要若干分钟或更长时间来生成和传输此备档,并且还需要足够大的本地磁盘空间。 +

    +
    + + + +\$hiddenStr + + 备档中所有文件具有相对路径,在 \${EscHTML(\$pathHdr eq "" ? "/" : \$pathHdr)} 目录内 +(否则备档中文件具有完整路径)。 +
    + +
    +EOF + + +# ------------------------------ +$Lang{Restore_Confirm_on__host} = "BackupPC: 客户机 \$host 开始恢复确认"; + +$Lang{Are_you_sure} = < +你即将开始恢复数据直接到客户机 \$In{hostDest} 上。 +储存在备份号 \$num 中的下列文件将被恢复到卷 \$In{shareDest} 内: +

    + + +\$fileListStr +
    原始文件/目录将被恢复到
    + +

    + + + + + + + +\$hiddenStr +你确定吗? + + +
    +EOF + + +# -------------------------- +$Lang{Restore_Requested_on__hostDest} = "BackupPC: 客户机 \$hostDest 有恢复请求"; +$Lang{Reply_from_server_was___reply} = < +服务器答复是:\$reply +

    +返回 \$hostDest 主页。 +EOF + +$Lang{BackupPC_Archive_Reply_from_server} = < +服务器答复是:\$reply +EOF + + +# ------------------------- +$Lang{Host__host_Backup_Summary} = "BackupPC: 客户机 \$host 备份报告"; + +$Lang{Host__host_Backup_Summary2} = < +\$warnStr +

      +\$statusStr +
    +

    +\${h2("用户操作")} +

    +

    + + +\$startIncrStr + + +
    +

    +\${h2("备份报告")} +

    +点击备份序列号浏览和恢复文件。 +

    + + + + + + + + + + +\$str +
    备份序列号# 类型 完整 备份级别 开始时间 耗时(分钟) 距离现在(天前) 服务器上备份路径
    +

    + +\$restoreStr +

    +

    +\${h2("传输错误报告")} +

    + + + + + + + + + +\$errStr +
    备份序列号# 类型 查看 传输错误数目 损坏文件数目 损坏文件系统卷数目 损坏 Tar 文件数目
    +

    + +\${h2("文件大小/数目统计")} +

    +"原有文件"是指原先已存在备份池中的文件;"新增文件"是指备份新写入池中的文件。 +空文件不被统计在内。 +

    + + + + + + + + + + + + + + + + + +\$sizeStr +
    合计 原有文件 新增文件
    备份序列号# 类型 文件数目 大小(MB) 备份速度(MB/sec) 文件数目 大小(MB) 文件数目 大小(MB)
    +

    + +\${h2("压缩报告")} +

    +备份池中原有文件和新增文件的压缩性能报告。 +

    + + + + + + + + + + + + + + + +\$compStr +
    原有文件 新增文件
    备份序列号# 类型 压缩级别 压缩前(MB) 压缩后(MB) 压缩比 压缩前(MB) 压缩后(MB) 压缩比
    +

    +EOF + +$Lang{Host__host_Archive_Summary} = "BackupPC: 客户机 \$host 备档报告"; +$Lang{Host__host_Archive_Summary2} = < +\$warnStr +
      +\$statusStr +
    + +\${h2("用户操作")} +

    +

    + + + + + +
    + +\$ArchiveStr + +EOF + +# ------------------------- +$Lang{Error} = "BackupPC: 错误"; +$Lang{Error____head} = <\$mesg

    +EOF + +# ------------------------- +$Lang{NavSectionTitle_} = "服务器"; + +# ------------------------- +$Lang{Backup_browse_for__host} = < + + + +
    + + + + +
      +
    • 你正在浏览备份 #\$num,该备份开始于 \$backupTime + (\$backupAge 天前)。 +\$filledBackup +
    • 进入目录: +
    • 点击目录名进入相应目录。 +
    • 点击文件名恢复相应文件。 +
    • 查看当前目录的备份历史。 +
    +
    + +\${h2("\${EscHTML(\$dirDisplay)} 的内容")} +
    + + + + + +
    + +
    + + \$dirStr +
    +
    + +
    + + \$fileHeader + \$topCheckAll + \$fileStr + \$checkAll +
    +
    +
    + +
    +EOF + +# ------------------------------ +$Lang{DirHistory_backup_for__host} = "BackupPC: 客户机 \$host 目录备份历史"; + +# +# These two strings are used to build the links for directories and +# file versions. Files are appended with a version number. +# +$Lang{DirHistory_dirLink} = "目录"; +$Lang{DirHistory_fileLink} = "v"; + +$Lang{DirHistory_for__host} = < +本页显示文件在所有备份中的不同版本: +
      +
    • 点击备份序列号返回相应备份浏览主页, +
    • 点击目录链接标记 (\$Lang->{DirHistory_dirLink}) 进入相应目录, +
    • 点击文件版本链接标记 (\$Lang->{DirHistory_fileLink}0, + \$Lang->{DirHistory_fileLink}1, ...) 下载相应文件, +
    • 如果一个文件的内容在多个备份中相同,文件在多个备份中具有相同版本号, +
    • 如果一个文件或目录在某个备份中不存在,下表中用空白表示, +
    • 具有相同版本号的文件可能在不同备份中有不同的文件属性。可以点击备份序列号来查看文件在相应备份中的属性。 +
    + +\${h2("\${EscHTML(\$dirDisplay)} 的历史")} + +
    + +\$backupNumStr +\$backupTimeStr +\$fileStr +
    备份序列号
    备份时间
    +EOF + +# ------------------------------ +$Lang{Restore___num_details_for__host} = "BackupPC: 客户机 \$host 恢复 #\$num 详情"; + +$Lang{Restore___num_details_for__host2} = < + + + + + + + + + + + + + + + + + + + +
    恢复序列号 \$Restores[\$i]{num}
    请求方 \$RestoreReq{user}
    请求时间 \$reqTime
    结果 \$Restores[\$i]{result}
    错误信息 \$Restores[\$i]{errorMsg}
    源客户机 \$RestoreReq{hostSrc}
    源备份序列号 \$RestoreReq{num}
    源文件卷 \$RestoreReq{shareSrc}
    目的客户机 \$RestoreReq{hostDest}
    目的文件卷 \$RestoreReq{shareDest}
    恢复开始时间 \$startTime
    耗时 \$duration 分钟
    文件个数 \$Restores[\$i]{nFiles}
    文件总大小 \${MB} MB
    传输速率 \$MBperSec MB/sec
    Tar 生成过程错误个数 \$Restores[\$i]{tarCreateErrs}
    传输过程错误个数 \$Restores[\$i]{xferErrs}
    传输日志文件 +查看日志, +查看错误 +
    +

    +\${h1("文件/目录列表")} +

    + + +\$fileListStr +
    原始文件/目录恢复至
    +EOF + +# ------------------------------ +$Lang{Archive___num_details_for__host} = "BackupPC: 客户机 \$host 备档 #\$num 详情"; + +$Lang{Archive___num_details_for__host2 } = < + + + + + + + + + +
    备档序列号 \$Archives[\$i]{num}
    请求方 \$ArchiveReq{user}
    请求方 \$reqTime
    结果 \$Archives[\$i]{result}
    错误信息 \$Archives[\$i]{errorMsg}
    开始时间 \$startTime
    耗时 \$duration 分钟
    传输日志文件 +查看日志, +查看错误 +
    +

    +\${h1("客户机列表")} +

    + + +\$HostListStr +
    客户机备份序列号
    +EOF + +# ----------------------------------- +$Lang{Email_Summary} = "BackupPC: 电子邮件报告"; + +# ----------------------------------- +# !! ERROR messages !! +# ----------------------------------- +$Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new 步骤失败:请检查 Apache 服务器日志\n"; +$Lang{Wrong_user__my_userid_is___} = + "错误用户:我的用户 ID 是 \$>, 不是 \$uid" + . "(\$Conf{BackupPCUser})\n"; +# $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; +$Lang{Only_privileged_users_can_stop_or_start_backups} = + "只有特权用户可以执行备份的开始或停止操作于客户机" + . " \${EscHTML(\$host)}."; +$Lang{Invalid_number__num} = "无效数字 \$num"; +$Lang{Unable_to_open__file__configuration_problem} = "无法打开文件 \$file:配置有误?"; +$Lang{Only_privileged_users_can_view_log_or_config_files} = "只有特权用户可以查看日志或配置文件。"; +$Lang{Only_privileged_users_can_view_log_files} = "只有特权用户可以查看日志文件。"; +$Lang{Only_privileged_users_can_view_email_summaries} = "只有特权用户可以查看电子邮件报告。"; +$Lang{Only_privileged_users_can_browse_backup_files} = "只有特权用户可以浏览" + . "客户机 \${EscHTML(\$In{host})} 的备份文件。"; +$Lang{Empty_host_name} = "空客户机名。"; +$Lang{Directory___EscHTML} = "目录 \${EscHTML(\"\$TopDir/pc/\$host/\$num\")}" + . " 为空"; +$Lang{Can_t_browse_bad_directory_name2} = "无法浏览非法目录名" + . " \${EscHTML(\$relDir)}"; +$Lang{Only_privileged_users_can_restore_backup_files} = "只有特权用户可以恢复" + . "客户机 \${EscHTML(\$In{host})} 的备份文件。"; +$Lang{Bad_host_name} = "错误客户机名 \${EscHTML(\$host)}"; +$Lang{You_haven_t_selected_any_files__please_go_Back_to} = "你还没有选择任何文件;请返回上一页" + . "选择文件。"; +$Lang{You_haven_t_selected_any_hosts} = "你还没有选择任何客户机;请返回上一页" + . "选择客户机。"; +$Lang{Nice_try__but_you_can_t_put} = "对不起,文件名内不能包含 \'..\'"; +$Lang{Host__doesn_t_exist} = "客户机 \${EscHTML(\$In{hostDest})} 不存在"; +$Lang{You_don_t_have_permission_to_restore_onto_host} = "你没有权限恢复客户机" + . " \${EscHTML(\$In{hostDest})}"; +$Lang{Can_t_open_create__openPath} = "无法打开/创建 " + . "\${EscHTML(\"\$openPath\")}"; +$Lang{Only_privileged_users_can_restore_backup_files2} = "只有特权用户可以恢复" + . "客户机 \${EscHTML(\$host)} 的备份文件。"; +$Lang{Empty_host_name} = "空客户机名"; +$Lang{Unknown_host_or_user} = "未知客户机或用户 \${EscHTML(\$host)}"; +$Lang{Only_privileged_users_can_view_information_about} = "只有特权用户可以查看" + . "客户机 \${EscHTML(\$host)} 的信息。" ; +$Lang{Only_privileged_users_can_view_archive_information} = "只有特权用户可以查看备档信息。"; +$Lang{Only_privileged_users_can_view_restore_information} = "只有特权用户可以查看恢复信息。"; +$Lang{Restore_number__num_for_host__does_not_exist} = "客户机 \${EscHTML(\$host)} 恢复序列号 \$num " + . "不存在。"; +$Lang{Archive_number__num_for_host__does_not_exist} = "客户机 \${EscHTML(\$host)} 备档序列号 \$num " + . "不存在。"; +$Lang{Can_t_find_IP_address_for} = "客户机 \${EscHTML(\$host)} 的 IP 地址无法找到"; +$Lang{host_is_a_DHCP_host} = < +除非获得客户机 \$host 的动态 IP 地址,否则只能从客户主机上发出此任务请求。 +EOF + +# ------------------------------------ +# !! Server Mesg !! +# ------------------------------------ + +$Lang{Backup_requested_on_DHCP__host} = "用户 \$User 从 \$ENV{REMOTE_ADDR} 发起请求备份使用动态 IP 的客户机 \$host (\$In{hostIP})"; +$Lang{Backup_requested_on__host_by__User} = "用户 \$User 发起请求备份客户机 \$host"; +$Lang{Backup_stopped_dequeued_on__host_by__User} = "用户 \$User 停止/取消了对客户机 \$host 的备份"; +$Lang{Restore_requested_to_host__hostDest__backup___num} = "用户 \$User 从 \$ENV{REMOTE_ADDR} 发起请求恢复客户机 \$hostDest,使用备份序列号 #\$num"; +$Lang{Archive_requested} = "用户 \$User 从 \$ENV{REMOTE_ADDR} 发起备档请求"; + +# ------------------------------------------------- +# ------- Stuff that was forgotten ---------------- +# ------------------------------------------------- + +$Lang{Status} = "状态"; +$Lang{PC_Summary} = "客户机报告"; +$Lang{LOG_file} = "日志文件"; +$Lang{LOG_files} = "日志文件列表"; +$Lang{Old_LOGs} = "旧日志"; +$Lang{Email_summary} = "电子邮件报告"; +$Lang{Config_file} = "配置文件"; +# $Lang{Hosts_file} = "Hosts file"; +$Lang{Current_queues} = "当前队列"; +$Lang{Documentation} = "文档资料"; + +#$Lang{Host_or_User_name} = "Host or User name:"; +$Lang{Go} = "确定"; +$Lang{Hosts} = "客户机"; +$Lang{Select_a_host} = "选择客户机名..."; + +$Lang{There_have_been_no_archives} = "

    这台机器还从来没有执行过备档操作!

    \n"; +$Lang{This_PC_has_never_been_backed_up} = "

    这台机器还从来没有被备份过!!

    \n"; +$Lang{This_PC_is_used_by} = "
  • 这台机器的用户包括 \${UserLink(\$user)}"; + +$Lang{Extracting_only_Errors} = "(只提取错误信息)"; +$Lang{XferLOG} = "传输日志"; +$Lang{Errors} = "错误"; + +# ------------ +$Lang{Last_email_sent_to__was_at___subject} = <给用户 \${UserLink(\$user)} 的最近一封邮件送出于 \$mailTime,标题是"\$subj"。 +EOF +# ------------ +$Lang{The_command_cmd_is_currently_running_for_started} = <命令 \$cmd 正在为客户机 \$host 运行,开始于 \$startTime。 +EOF + +# ----------- +$Lang{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon} = <客户机 \$host 已在后台队列中等待(即将被备份)。 +EOF + +# ---------- +$Lang{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon} = <客户机 \$host 已在用户队列中等待(即将被备份)。 +EOF + +# --------- +$Lang{A_command_for_host_is_on_the_command_queue_will_run_soon} = <针对客户机 \$host 的一条命令已在命令队列中等待(即将被执行)。 +EOF + +# -------- +$Lang{Last_status_is_state_StatusHost_state_reason_as_of_startTime} = <最后状态是 \"\$Lang->{\$StatusHost{state}}\"\$reason,当时时间 \$startTime。 +EOF + +# -------- +$Lang{Last_error_is____EscHTML_StatusHost_error} = <最后错误信息是 \"\${EscHTML(\$StatusHost{error})}\"。 +EOF + +# ------ +$Lang{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times} = <试图与客户机 \$host 联系(Ping 操作)已连续失败 \$StatusHost{deadCnt} 次。 +EOF + +# ----- +$Lang{Prior_to_that__pings} = "先前,Ping"; + +# ----- +$Lang{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times} = <\$priorStr 客户机 \$host 已连续成功 \$StatusHost{aliveCnt} 次。 +EOF + +$Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <因为客户机 \$host 已经在网络上至少连续 \$Conf{BlackoutGoodCnt} 次, +在下列时段 \$blackoutStr,它将不进行备份操作。 +EOF + +$Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 在 \$days"; + +$Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <备份被推迟 \$hours 小时 +(改变时间)。 +EOF + +$Lang{tryIP} = " 和 \$StatusHost{dhcpHostIP}"; + +# $Lang{Host_Inhost} = "Host \$In{host}"; + +$Lang{checkAll} = < + å…¨é€‰ + + + +EOF + +$Lang{checkAllHosts} = < + å…¨é€‰ + + + +EOF + +$Lang{fileHeader} = < 文件/目录名 + 类型 + 读写权限 + 备份序列号 + 大小 + 修改日期 + +EOF + +$Lang{Home} = "主页"; +$Lang{Browse} = "浏览备份"; +$Lang{Last_bad_XferLOG} = "最近出错传输日志"; +$Lang{Last_bad_XferLOG_errors_only} = "最近出错传输日志(只含错误)"; + +$Lang{This_display_is_merged_with_backup} = < 本页显示的是与备份序列 #\$numF 合成的结果。 +EOF + +$Lang{Visit_this_directory_in_backup} = < 选择你想查看的备份: +EOF + +$Lang{Restore_Summary} = < +点击恢复序列号获取详情。 + + + + + + + + + + +\$restoreStr +
    恢复序列号 结果 开始时间 耗时(分钟) 文件个数 大小(MB) Tar 错误个数 传输错误个数
    +

    +EOF + +$Lang{Archive_Summary} = < +点击备档序列号获取详情。 + + + + + + +\$ArchiveStr +
    备档序列号 结果 开始时间 耗时(分钟)
    +

    +EOF + +$Lang{BackupPC__Documentation} = "BackupPC: 文档资料"; + +$Lang{No} = "否"; +$Lang{Yes} = "是"; + +$Lang{The_directory_is_empty} = <目录 \${EscHTML(\$dirDisplay)} 是空目录 + +EOF + +#$Lang{on} = "开"; +$Lang{off} = "关"; + +$Lang{backupType_full} = "完全"; +$Lang{backupType_incr} = "增量"; +$Lang{backupType_partial} = "部分"; + +$Lang{failed} = "失败"; +$Lang{success} = "成功"; +$Lang{and} = "和"; + +# ------ +# Hosts states and reasons +$Lang{Status_idle} = "空闲"; +$Lang{Status_backup_starting} = "备份已开始"; +$Lang{Status_backup_in_progress} = "备份进行中"; +$Lang{Status_restore_starting} = "恢复已开始"; +$Lang{Status_restore_in_progress} = "恢复进行中"; +$Lang{Status_link_pending} = "文件链接待建立"; +$Lang{Status_link_running} = "文件链接建立中"; + +$Lang{Reason_backup_done} = "完成"; +$Lang{Reason_restore_done} = "恢复完成"; +$Lang{Reason_archive_done} = "备档完成"; +$Lang{Reason_nothing_to_do} = "空闲"; +$Lang{Reason_backup_failed} = "备份失败"; +$Lang{Reason_restore_failed} = "恢复失败"; +$Lang{Reason_archive_failed} = "备档失败"; +$Lang{Reason_no_ping} = "网络连接中断(no ping)"; +$Lang{Reason_backup_canceled_by_user} = "备份被用户取消"; +$Lang{Reason_restore_canceled_by_user} = "恢复被用户取消"; +$Lang{Reason_archive_canceled_by_user} = "备档被用户取消"; +$Lang{Disabled_OnlyManualBackups} = "自动备份被关闭"; +$Lang{Disabled_AllBackupsDisabled} = "关闭"; + + +# --------- +# Email messages + +# No backup ever +$Lang{EMailNoBackupEverSubj} = "BackupPC: 客户机 \$host 从未被成功备份过"; +$Lang{EMailNoBackupEverMesg} = <<'EOF'; +To: $user$domain +cc: +Subject: $subj +$headers +尊敬的用户 $userName, + +您的电脑 ($host) 还从来没有被我们的备份系统成功备份过。 +正常情况下,当您的电脑与网络连接时电脑备份会自动进行。 +如果您属于下面两种情况,请与系统管理员联系: + + - 您的电脑经常是连在网络上的。这意味着可能是某些配置 + 方面的问题导致备份无法进行。 + + - 您不希望您的电脑被备份,并且不愿再收到这些电子邮件。 + +如果不是以上这些情况,请确认您的电脑是被连接在网络上的。 + +此致敬礼, + +BackupPC Genie +http://backuppc.sourceforge.net +EOF + +# No recent backup +$Lang{EMailNoBackupRecentSubj} = "BackupPC: 客户机 \$host 最近未被备份过"; +$Lang{EMailNoBackupRecentMesg} = <<'EOF'; +To: $user$domain +cc: +Subject: $subj +$headers +尊敬的用户 $userName, + +您的电脑 ($host) 已经有 $days 天没有被成功备份过了。您的电脑 +第一次被备份是在 $firstTime 天前,直至 $days 天前已经被备份过 $numBackups 次。 +正常情况下,当您的电脑与网络连接时电脑备份会自动进行。 + +在最近 $days 天内,如果您的电脑已经与网络连接了若干小时, +请与系统管理员联系以判断为什么备份没有进行。 + +除此之外,如果您不在办公室,您只能手动拷贝重要文件到其它存储介质上。 +应该提醒您的是,如果您的电脑磁盘损坏,您在最近 $days 天内创建或修改 +的文件,包括新收到的电子邮件和附件,将无法被恢复。 + +此致敬礼, + +BackupPC Genie +http://backuppc.sourceforge.net +EOF + +# Old Outlook files +$Lang{EMailOutlookBackupSubj} = "BackupPC: 客户机 \$host 上的微软 Outlook 文件需要备份"; +$Lang{EMailOutlookBackupMesg} = <<'EOF'; +To: $user$domain +cc: +Subject: $subj +$headers +尊敬的用户 $userName, + +您的电脑上的 Outlook 文件 $howLong。 + +这些文件包括所有您的电子邮件,附件,通讯录及日程表信息。 +您的电脑第一次被备份是在 $firstTime 天前,直至 $lastTime 天前已经被 +备份过 $numBackups 次。但是,Outlook 在运行时锁住所有所属文件, +导致这些文件无法被备份。 + +建议您依以下方式备份 Outlook 文件: + +1。首先确认电脑是连接在网路上; +2。退出 Outlook 及所有其它应用; +3。使用网页浏览器访问此链接: + + $CgiURL?host=$host + +选择 “开始增量备份”,启动增量备份操作;然后选择 “返回 $host 主页” +并用浏览器的 “刷新” 功能来检查该备份操作的状态。 + +此致敬礼, + +BackupPC Genie +http://backuppc.sourceforge.net +EOF + +$Lang{howLong_not_been_backed_up} = "还从未被成功备份过"; +$Lang{howLong_not_been_backed_up_for_days_days} = "已经有 \$days 天未被备份过"; + +####################################################################### +# RSS strings +####################################################################### +$Lang{RSS_Doc_Title} = "BackupPC 服务器"; +$Lang{RSS_Doc_Description} = "RSS feed for BackupPC"; +$Lang{RSS_Host_Summary} = < +注意:适用于所有客户机的全局性默认配置,其相应 “替换” 旁的方框是不被选择的。如果要修改本客户机的某项设置,请点击 “替换” 旁的方框。如果该设置已经处于被修改状态,则修改后不需点击 “替换” 旁的方框。如果要将其还原使用默认配置,则需点击 “替换” 旁的方框,使其处于未被修改状态。 +

    +EOF + +$Lang{CfgEdit_Button_Save} = "保存"; +$Lang{CfgEdit_Button_Insert} = "插入"; +$Lang{CfgEdit_Button_Delete} = "删除"; +$Lang{CfgEdit_Button_Add} = "添加"; +$Lang{CfgEdit_Button_Override} = "替换"; +$Lang{CfgEdit_Button_New_Key} = "文件卷名(Windows Share)"; + +$Lang{CfgEdit_Error_No_Save} + = "错误:有误,无法保存"; +$Lang{CfgEdit_Error__must_be_an_integer} + = "错误:\$var 必须是整数"; +$Lang{CfgEdit_Error__must_be_real_valued_number} + = "错误:\$var 必须是实数,不能是浮点数"; +$Lang{CfgEdit_Error__entry__must_be_an_integer} + = "错误:\$var 内容 \$k 必须是整数"; +$Lang{CfgEdit_Error__entry__must_be_real_valued_number} + = "错误:\$var 内容 \$k 必须是实数,不能是浮点数"; +$Lang{CfgEdit_Error__must_be_executable_program} + = "错误:\$var 必须是可执行程序"; +$Lang{CfgEdit_Error__must_be_valid_option} + = "错误:\$var 必须是合法选项"; +$Lang{CfgEdit_Error_Copy_host_does_not_exist} + = "客户机 \$copyHost 不存在;生成全计算机名 \$fullHost。如果此客户机不是你想要的,请将它删除。"; + +$Lang{CfgEdit_Log_Copy_host_config} + = "用户 \$User 拷贝了客户机 \$fromHost 的配置到客户机 \$host\n"; +$Lang{CfgEdit_Log_Delete_param} + = "用户 \$User 从配置 \$conf 中删除了 \$p\n"; +$Lang{CfgEdit_Log_Add_param_value} + = "用户 \$User 添加了 \$p 到配置 \$conf 中,值设为 \$value\n"; +$Lang{CfgEdit_Log_Change_param_value} + = "用户 \$User 将配置 \$conf 中的 \$p 从 \$valueOld 更改为 \$valueNew\n"; +$Lang{CfgEdit_Log_Host_Delete} + = "用户 \$User 删除了客户机 \$host\n"; +$Lang{CfgEdit_Log_Host_Change} + = "用户 \$User 将客户机 \$host 上的 \$key 从 \$valueOld 更改为 \$valueNew\n"; +$Lang{CfgEdit_Log_Host_Add} + = "用户 \$User 添加了客户机 \$host: \$value\n"; + +#end of lang_zh_CN.pm diff --git a/lib/BackupPC/Lib.pm b/lib/BackupPC/Lib.pm index 44de3c5..e1dda82 100644 --- a/lib/BackupPC/Lib.pm +++ b/lib/BackupPC/Lib.pm @@ -49,6 +49,7 @@ use Socket; use Cwd; use Digest::MD5; use Config; +use Encode; use vars qw( $IODirentOk ); use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -451,6 +452,10 @@ sub HostsMTime # $need is a hash of file attributes we need: type, inode, or nlink. # If set, these parameters are added to the returned hash. # +# To support browsing pre-3.0.0 backups where the charset encoding +# is typically iso-8859-1, the charsetLegacy option can be set in +# $need to convert the path from utf8 and convert the names to utf8. +# # If IO::Dirent is successful if will get type and inode for free. # Otherwise, a stat is done on each file, which is more expensive. # @@ -459,6 +464,8 @@ sub dirRead my($bpc, $path, $need) = @_; my(@entries, $addInode); + from_to($path, "utf8", $need->{charsetLegacy}) + if ( $need->{charsetLegacy} ne "" ); return if ( !opendir(my $fh, $path) ); if ( $IODirentOk ) { @entries = sort({ $a->{inode} <=> $b->{inode} } readdirent($fh)); @@ -495,6 +502,14 @@ sub dirRead # sorted above) # @entries = sort({ $a->{inode} <=> $b->{inode} } @entries) if ( $addInode ); + # + # for browing pre-3.0.0 backups, map iso-8859-1 to utf8 if requested + # + if ( $need->{charsetLegacy} ne "" ) { + for ( my $i = 0 ; $i < @entries ; $i++ ) { + from_to($entries[$i]{name}, $need->{charsetLegacy}, "utf8"); + } + } return \@entries; } @@ -504,9 +519,9 @@ sub dirRead # sub dirReadNames { - my($bpc, $path) = @_; + my($bpc, $path, $need) = @_; - my $entries = $bpc->dirRead($path); + my $entries = $bpc->dirRead($path, $need); return if ( !defined($entries) ); my @names = map { $_->{name} } @$entries; return \@names; @@ -711,7 +726,10 @@ sub ServerMesg { my($bpc, $mesg) = @_; return if ( !defined(my $fh = $bpc->{ServerFD}) ); + $mesg =~ s/\n/\\n/g; + $mesg =~ s/\r/\\r/g; my $md5 = Digest::MD5->new; + $mesg = encode_utf8($mesg); $md5->add($bpc->{ServerSeed} . $bpc->{ServerMesgCnt} . $bpc->{Conf}{ServerMesgSecret} . $mesg); print($fh $md5->b64digest . " $mesg\n"); diff --git a/lib/BackupPC/Storage.pm b/lib/BackupPC/Storage.pm index e54802b..0f7a43e 100644 --- a/lib/BackupPC/Storage.pm +++ b/lib/BackupPC/Storage.pm @@ -52,7 +52,7 @@ sub new xferErrs xferBadFile xferBadShare tarErrs compress sizeExistComp sizeNewComp noFill fillFromNum mangle xferMethod level - charset + charset version )], RestoreFields => [qw( num startTime endTime result errorMsg nFiles size diff --git a/lib/BackupPC/Storage/Text.pm b/lib/BackupPC/Storage/Text.pm index 4dea60e..b99648d 100644 --- a/lib/BackupPC/Storage/Text.pm +++ b/lib/BackupPC/Storage/Text.pm @@ -84,6 +84,19 @@ sub BackupInfoRead close(BK_INFO); } close(LOCK); + # + # Default the level and version fields if not present + # + for ( my $i = 0 ; $i < @Backups ; $i++ ) { + if ( defined($Backups[$i]{level}) ) { + if ( !defined($Backups[$i]{version}) ) { + $Backups[$i]{version} = "3.0.0"; + } + } else { + $Backups[$i]{level} = $Backups[$i]{type} eq "incr" ? 1 : 0; + $Backups[$i]{version} = "2.1.2"; + } + } return @Backups; } @@ -279,6 +292,7 @@ sub ConfigDataRead } %$conf = ( %$conf, %Conf ); } + # # Promote BackupFilesOnly and BackupFilesExclude to hashes # @@ -306,6 +320,11 @@ sub ConfigDataRead delete($conf->{BlackoutWeekDays}); } + # + # Make sure IncrLevels is defined + # + $conf->{IncrLevels} = [1] if ( !defined($conf->{IncrLevels}) ); + return (undef, $conf); } diff --git a/lib/BackupPC/View.pm b/lib/BackupPC/View.pm index cf03983..64d5abe 100644 --- a/lib/BackupPC/View.pm +++ b/lib/BackupPC/View.pm @@ -64,10 +64,6 @@ sub new # are added to the returned hash. # See BackupPC::Lib::dirRead(). }, $class; - for ( my $i = 0 ; $i < @{$m->{backups}} ; $i++ ) { - next if ( defined($m->{backups}[$i]{level}) ); - $m->{backups}[$i]{level} = $m->{backups}[$i]{type} eq "incr" ? 1 : 0; - } $m->{topDir} = $m->{bpc}->TopDir(); return $m; } @@ -138,7 +134,7 @@ sub dirCache my $attr; if ( $mangle ) { $attr = BackupPC::Attrib->new({ compress => $compress }); - if ( -f $attr->fileName($path) && !$attr->read($path) ) { + if ( !$attr->read($path) ) { $m->{error} = "Can't read attribute file in $path"; $attr = undef; } @@ -402,7 +398,7 @@ sub dirHistory my $attr; if ( $mangle ) { $attr = BackupPC::Attrib->new({ compress => $compress }); - if ( -f $attr->fileName($path) && !$attr->read($path) ) { + if ( !$attr->read($path) ) { $m->{error} = "Can't read attribute file in $path"; $attr = undef; } diff --git a/lib/BackupPC/Xfer/RsyncFileIO.pm b/lib/BackupPC/Xfer/RsyncFileIO.pm index 044ee55..221fd3f 100644 --- a/lib/BackupPC/Xfer/RsyncFileIO.pm +++ b/lib/BackupPC/Xfer/RsyncFileIO.pm @@ -436,6 +436,8 @@ sub attribSet my($fio, $f, $placeHolder) = @_; my($dir, $file); + return if ( $placeHolder && $fio->{phase} > 0 ); + if ( $f->{name} =~ m{(.*)/(.*)}s ) { $file = $2; $dir = "$fio->{shareM}/" . $1; @@ -447,10 +449,13 @@ sub attribSet $file = $f->{name}; } - if ( !defined($fio->{attribLastDir}) || $fio->{attribLastDir} ne $dir ) { + if ( $dir ne "" + && (!defined($fio->{attribLastDir}) || $fio->{attribLastDir} ne $dir) ) { # # Flush any directories that don't match the first part - # of the new directory + # of the new directory. Don't flush the top-level directory + # (ie: $dir eq "") since the "." might get sorted in the middle + # of other top-level directories or files. # foreach my $d ( keys(%{$fio->{attrib}}) ) { next if ( $d eq "" || "$dir/" =~ m{^\Q$d/} ); @@ -459,17 +464,29 @@ sub attribSet $fio->{attribLastDir} = $dir; } if ( !exists($fio->{attrib}{$dir}) ) { + $fio->log("attribSet: dir=$dir not found") if ( $fio->{logLevel} >= 4 ); $fio->{attrib}{$dir} = BackupPC::Attrib->new({ compress => $fio->{xfer}{compress}, }); - my $path = $fio->{outDir} . $dir; - if ( -f $fio->{attrib}{$dir}->fileName($path) - && !$fio->{attrib}{$dir}->read($path) ) { - $fio->log(sprintf("Unable to read attribute file %s", + my $dirM = $dir; + $dirM = $1 . "/" . $fio->{bpc}->fileNameMangle($2) + if ( $dirM =~ m{(.*?)/(.*)}s ); + my $path = $fio->{outDir} . $dirM; + if ( -f $fio->{attrib}{$dir}->fileName($path) ) { + if ( !$fio->{attrib}{$dir}->read($path) ) { + $fio->log(sprintf("Unable to read attribute file %s", $fio->{attrib}{$dir}->fileName($path))); + } else { + $fio->log(sprintf("attribRead file %s", + $fio->{attrib}{$dir}->fileName($path))) + if ( $fio->{logLevel} >= 4 ); + } } + } else { + $fio->log("attribSet: dir=$dir exists") if ( $fio->{logLevel} >= 4 ); } - $fio->log("attribSet(dir=$dir, file=$file)") if ( $fio->{logLevel} >= 4 ); + $fio->log("attribSet(dir=$dir, file=$file, size=$f->{size}, placeholder=$placeHolder)") + if ( $fio->{logLevel} >= 4 ); my $mode = $f->{mode}; @@ -490,11 +507,6 @@ sub attribWrite my($fio, $d) = @_; my($poolWrite); - # - # Don't write attributes on 2nd phase - they're already - # taken care of during the first phase. - # - return if ( $fio->{phase} > 0 ); if ( !defined($d) ) { # # flush all entries (in reverse order) @@ -505,6 +517,7 @@ sub attribWrite return; } return if ( !defined($fio->{attrib}{$d}) ); + # # Set deleted files in the attributes. Any file in the view # that doesn't have attributes is flagged as deleted for @@ -541,7 +554,7 @@ sub attribWrite }) if ( $fio->{logLevel} >= 2 && $a->{type} == BPC_FTYPE_FILE ); } - } elsif ( !$fio->{full} ) { + } elsif ( $fio->{phase} == 0 && !$fio->{full} ) { ##print("Delete file $f\n"); $fio->logFileAction("delete", { %{$fio->{viewCache}{$d}{$f}}, @@ -559,7 +572,7 @@ sub attribWrite } } } - if ( $fio->{attrib}{$d}->fileCount ) { + if ( $fio->{attrib}{$d}->fileCount || $fio->{phase} > 0 ) { my $data = $fio->{attrib}{$d}->writeData; my $dirM = $d; diff --git a/makeDist b/makeDist index ad21c6f..506fb5d 100755 --- a/makeDist +++ b/makeDist @@ -292,6 +292,8 @@ sub InstallFile print OUT "$1'__TOPDIR__'$2\n"; } elsif ( $file =~ /Lib.pm/ && /^(\s*\$installDir\s*=\s*)'.*'(\s*if\s.*)/ ) { print OUT "$1'__INSTALLDIR__'$2\n"; + } elsif ( $file =~ /Lib.pm/ && /^(\s*ConfDir\s*=\>\s*\$confDir eq.*)'.*'(.*)/ ) { + print OUT "$1'__CONFDIR__'$2\n"; } elsif ( $file =~ /Lib.pm/ && /^(\s*my \$useFHS\s*=\s*)\d;/ ) { print OUT "${1}0;\n"; } elsif ( $file =~ /Lib.pm/ && /(.*Version *=> .*)'[\w\d\.]+',/ ) { -- 2.20.1