1 |
#============================================================= -*-perl-*- |
2 |
# |
3 |
# BackupPC::CGI::Lib package |
4 |
# |
5 |
# DESCRIPTION |
6 |
# |
7 |
# This library defines a BackupPC::Lib class and a variety of utility |
8 |
# functions used by BackupPC. |
9 |
# |
10 |
# AUTHOR |
11 |
# Craig Barratt <cbarratt@users.sourceforge.net> |
12 |
# |
13 |
# COPYRIGHT |
14 |
# Copyright (C) 2003 Craig Barratt |
15 |
# |
16 |
# This program is free software; you can redistribute it and/or modify |
17 |
# it under the terms of the GNU General Public License as published by |
18 |
# the Free Software Foundation; either version 2 of the License, or |
19 |
# (at your option) any later version. |
20 |
# |
21 |
# This program is distributed in the hope that it will be useful, |
22 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 |
# GNU General Public License for more details. |
25 |
# |
26 |
# You should have received a copy of the GNU General Public License |
27 |
# along with this program; if not, write to the Free Software |
28 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
29 |
# |
30 |
#======================================================================== |
31 |
# |
32 |
# Version 2.1.2, released 5 Sep 2005. |
33 |
# |
34 |
# See http://backuppc.sourceforge.net. |
35 |
# |
36 |
#======================================================================== |
37 |
|
38 |
package BackupPC::CGI::Lib; |
39 |
|
40 |
use strict; |
41 |
use BackupPC::Lib; |
42 |
|
43 |
require Exporter; |
44 |
|
45 |
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); |
46 |
|
47 |
use vars qw($Cgi %In $MyURL $User %Conf $TopDir $BinDir $bpc); |
48 |
use vars qw(%Status %Info %Jobs @BgQueue @UserQueue @CmdQueue |
49 |
%QueueLen %StatusHost); |
50 |
use vars qw($Hosts $HostsMTime $ConfigMTime $PrivAdmin); |
51 |
use vars qw(%UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq); |
52 |
use vars qw($Lang); |
53 |
|
54 |
@ISA = qw(Exporter); |
55 |
|
56 |
@EXPORT = qw( ); |
57 |
|
58 |
@EXPORT_OK = qw( |
59 |
timeStamp2 |
60 |
HostLink |
61 |
UserLink |
62 |
EscHTML |
63 |
EscURI |
64 |
ErrorExit |
65 |
ServerConnect |
66 |
GetStatusInfo |
67 |
ReadUserEmailInfo |
68 |
CheckPermission |
69 |
GetUserHosts |
70 |
ConfirmIPAddress |
71 |
Header |
72 |
Trailer |
73 |
NavSectionTitle |
74 |
NavSectionStart |
75 |
NavSectionEnd |
76 |
NavLink |
77 |
h1 |
78 |
h2 |
79 |
$Cgi %In $MyURL $User %Conf $TopDir $BinDir $bpc |
80 |
%Status %Info %Jobs @BgQueue @UserQueue @CmdQueue |
81 |
%QueueLen %StatusHost |
82 |
$Hosts $HostsMTime $ConfigMTime $PrivAdmin |
83 |
%UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq |
84 |
$Lang |
85 |
); |
86 |
|
87 |
%EXPORT_TAGS = ( |
88 |
'all' => [ @EXPORT_OK ], |
89 |
); |
90 |
|
91 |
sub NewRequest |
92 |
{ |
93 |
$Cgi = new CGI; |
94 |
%In = $Cgi->Vars; |
95 |
|
96 |
if ( !defined($bpc) ) { |
97 |
ErrorExit($Lang->{BackupPC__Lib__new_failed__check_apache_error_log}) |
98 |
if ( !($bpc = BackupPC::Lib->new(undef, undef, 1)) ); |
99 |
$TopDir = $bpc->TopDir(); |
100 |
$BinDir = $bpc->BinDir(); |
101 |
%Conf = $bpc->Conf(); |
102 |
$Lang = $bpc->Lang(); |
103 |
$ConfigMTime = $bpc->ConfigMTime(); |
104 |
} elsif ( $bpc->ConfigMTime() != $ConfigMTime ) { |
105 |
$bpc->ConfigRead(); |
106 |
%Conf = $bpc->Conf(); |
107 |
$Lang = $bpc->Lang(); |
108 |
$ConfigMTime = $bpc->ConfigMTime(); |
109 |
} |
110 |
|
111 |
# |
112 |
# Default REMOTE_USER so in a miminal installation the user |
113 |
# has a sensible default. |
114 |
# |
115 |
$ENV{REMOTE_USER} = $Conf{BackupPCUser} if ( $ENV{REMOTE_USER} eq "" ); |
116 |
|
117 |
# |
118 |
# We require that Apache pass in $ENV{SCRIPT_NAME} and $ENV{REMOTE_USER}. |
119 |
# The latter requires .ht_access style authentication. Replace this |
120 |
# code if you are using some other type of authentication, and have |
121 |
# a different way of getting the user name. |
122 |
# |
123 |
$MyURL = $ENV{SCRIPT_NAME}; |
124 |
$User = $ENV{REMOTE_USER}; |
125 |
|
126 |
# |
127 |
# Clean up %ENV for taint checking |
128 |
# |
129 |
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; |
130 |
$ENV{PATH} = $Conf{MyPath}; |
131 |
|
132 |
# |
133 |
# Verify we are running as the correct user |
134 |
# |
135 |
if ( $Conf{BackupPCUserVerify} |
136 |
&& $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { |
137 |
ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}"), <<EOF); |
138 |
This script needs to run as the user specified in \$Conf{BackupPCUser}, |
139 |
which is set to $Conf{BackupPCUser}. |
140 |
<p> |
141 |
This is an installation problem. If you are using mod_perl then |
142 |
it appears that Apache is not running as user $Conf{BackupPCUser}. |
143 |
If you are not using mod_perl, then most like setuid is not working |
144 |
properly on BackupPC_Admin. Check the permissions on |
145 |
$Conf{CgiDir}/BackupPC_Admin and look at the documentation. |
146 |
EOF |
147 |
} |
148 |
|
149 |
if ( !defined($Hosts) || $bpc->HostsMTime() != $HostsMTime ) { |
150 |
$HostsMTime = $bpc->HostsMTime(); |
151 |
$Hosts = $bpc->HostInfoRead(); |
152 |
|
153 |
# turn moreUsers list into a hash for quick lookups |
154 |
foreach my $host (keys %$Hosts) { |
155 |
$Hosts->{$host}{moreUsers} = |
156 |
{map {$_, 1} split(",", $Hosts->{$host}{moreUsers}) } |
157 |
} |
158 |
} |
159 |
|
160 |
# |
161 |
# Untaint the host name |
162 |
# |
163 |
if ( $In{host} =~ /^([\w.\s-]+)$/ ) { |
164 |
$In{host} = $1; |
165 |
} else { |
166 |
delete($In{host}); |
167 |
} |
168 |
} |
169 |
|
170 |
sub timeStamp2 |
171 |
{ |
172 |
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) |
173 |
= localtime($_[0] == 0 ? time : $_[0] ); |
174 |
$mon++; |
175 |
if ( $Conf{CgiDateFormatMMDD} ) { |
176 |
return sprintf("$mon/$mday %02d:%02d", $hour, $min); |
177 |
} else { |
178 |
return sprintf("$mday/$mon %02d:%02d", $hour, $min); |
179 |
} |
180 |
} |
181 |
|
182 |
sub HostLink |
183 |
{ |
184 |
my($host) = @_; |
185 |
my($s); |
186 |
if ( defined($Hosts->{$host}) || defined($Status{$host}) ) { |
187 |
$s = "<a href=\"$MyURL?host=${EscURI($host)}\">$host</a>"; |
188 |
} else { |
189 |
$s = $host; |
190 |
} |
191 |
return \$s; |
192 |
} |
193 |
|
194 |
sub UserLink |
195 |
{ |
196 |
my($user) = @_; |
197 |
my($s); |
198 |
|
199 |
return \$user if ( $user eq "" |
200 |
|| $Conf{CgiUserUrlCreate} eq "" ); |
201 |
if ( $Conf{CgiUserHomePageCheck} eq "" |
202 |
|| -f sprintf($Conf{CgiUserHomePageCheck}, $user, $user, $user) ) { |
203 |
$s = "<a href=\"" |
204 |
. sprintf($Conf{CgiUserUrlCreate}, $user, $user, $user) |
205 |
. "\">$user</a>"; |
206 |
} else { |
207 |
$s = $user; |
208 |
} |
209 |
return \$s; |
210 |
} |
211 |
|
212 |
sub EscHTML |
213 |
{ |
214 |
my($s) = @_; |
215 |
$s =~ s/&/&/g; |
216 |
$s =~ s/\"/"/g; |
217 |
$s =~ s/>/>/g; |
218 |
$s =~ s/</</g; |
219 |
$s =~ s{([^[:print:]])}{sprintf("&\#x%02X;", ord($1));}eg; |
220 |
return \$s; |
221 |
} |
222 |
|
223 |
sub EscURI |
224 |
{ |
225 |
my($s) = @_; |
226 |
$s =~ s{([^\w.\/-])}{sprintf("%%%02X", ord($1));}eg; |
227 |
return \$s; |
228 |
} |
229 |
|
230 |
sub ErrorExit |
231 |
{ |
232 |
my(@mesg) = @_; |
233 |
my($head) = shift(@mesg); |
234 |
my($mesg) = join("</p>\n<p>", @mesg); |
235 |
|
236 |
if ( !defined($ENV{REMOTE_USER}) ) { |
237 |
$mesg .= <<EOF; |
238 |
<p> |
239 |
Note: \$ENV{REMOTE_USER} is not set, which could mean there is an |
240 |
installation problem. BackupPC_Admin expects Apache to authenticate |
241 |
the user and pass their user name into this script as the REMOTE_USER |
242 |
environment variable. See the documentation. |
243 |
EOF |
244 |
} |
245 |
|
246 |
$bpc->ServerMesg("log User $User (host=$In{host}) got CGI error: $head") |
247 |
if ( defined($bpc) ); |
248 |
if ( !defined($Lang->{Error}) ) { |
249 |
$mesg = <<EOF if ( !defined($mesg) ); |
250 |
There is some problem with the BackupPC installation. |
251 |
Please check the permissions on BackupPC_Admin. |
252 |
EOF |
253 |
my $content = <<EOF; |
254 |
${h1("Error: Unable to read config.pl or language strings!!")} |
255 |
<p>$mesg</p> |
256 |
EOF |
257 |
Header("BackupPC: Error", $content); |
258 |
Trailer(); |
259 |
} else { |
260 |
my $content = eval("qq{$Lang->{Error____head}}"); |
261 |
Header(eval("qq{$Lang->{Error}}"), $content); |
262 |
Trailer(); |
263 |
} |
264 |
exit(1); |
265 |
} |
266 |
|
267 |
sub ServerConnect |
268 |
{ |
269 |
# |
270 |
# Verify that the server connection is ok |
271 |
# |
272 |
return if ( $bpc->ServerOK() ); |
273 |
$bpc->ServerDisconnect(); |
274 |
if ( my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ) { |
275 |
if ( CheckPermission() |
276 |
&& -f $Conf{ServerInitdPath} |
277 |
&& $Conf{ServerInitdStartCmd} ne "" ) { |
278 |
my $content = eval("qq{$Lang->{Admin_Start_Server}}"); |
279 |
Header(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}"), $content); |
280 |
Trailer(); |
281 |
exit(1); |
282 |
} else { |
283 |
ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); |
284 |
} |
285 |
} |
286 |
} |
287 |
|
288 |
sub GetStatusInfo |
289 |
{ |
290 |
my($status) = @_; |
291 |
ServerConnect(); |
292 |
%Status = () if ( $status =~ /\bhosts\b/ ); |
293 |
%StatusHost = () if ( $status =~ /\bhost\(/ ); |
294 |
my $reply = $bpc->ServerMesg("status $status"); |
295 |
$reply = $1 if ( $reply =~ /(.*)/s ); |
296 |
eval($reply); |
297 |
# ignore status related to admin and trashClean jobs |
298 |
if ( $status =~ /\bhosts\b/ ) { |
299 |
foreach my $host ( grep(/admin/, keys(%Status)) ) { |
300 |
delete($Status{$host}) if ( $bpc->isAdminJob($host) ); |
301 |
} |
302 |
delete($Status{$bpc->trashJob}); |
303 |
} |
304 |
} |
305 |
|
306 |
sub ReadUserEmailInfo |
307 |
{ |
308 |
if ( (stat("$TopDir/log/UserEmailInfo.pl"))[9] != $UserEmailInfoMTime ) { |
309 |
do "$TopDir/log/UserEmailInfo.pl"; |
310 |
$UserEmailInfoMTime = (stat("$TopDir/log/UserEmailInfo.pl"))[9]; |
311 |
} |
312 |
} |
313 |
|
314 |
# |
315 |
# Check if the user is privileged. A privileged user can access |
316 |
# any information (backup files, logs, status pages etc). |
317 |
# |
318 |
# A user is privileged if they belong to the group |
319 |
# $Conf{CgiAdminUserGroup}, or they are in $Conf{CgiAdminUsers} |
320 |
# or they are the user assigned to a host in the host file. |
321 |
# |
322 |
sub CheckPermission |
323 |
{ |
324 |
my($host) = @_; |
325 |
my $Privileged = 0; |
326 |
|
327 |
return 0 if ( $User eq "" && $Conf{CgiAdminUsers} ne "*" |
328 |
|| $host ne "" && !defined($Hosts->{$host}) ); |
329 |
if ( $Conf{CgiAdminUserGroup} ne "" ) { |
330 |
my($n,$p,$gid,$mem) = getgrnam($Conf{CgiAdminUserGroup}); |
331 |
$Privileged ||= ($mem =~ /\b$User\b/); |
332 |
|
333 |
# check against REMOTE_NTGROUP from mod_ntlm |
334 |
$Privileged ||= $Conf{CgiAdminUserGroup} eq $ENV{REMOTE_NTGROUP}; |
335 |
} |
336 |
if ( $Conf{CgiAdminUsers} ne "" ) { |
337 |
$Privileged ||= ($Conf{CgiAdminUsers} =~ /\b$User\b/); |
338 |
$Privileged ||= $Conf{CgiAdminUsers} eq "*"; |
339 |
} |
340 |
$PrivAdmin = $Privileged; |
341 |
return $Privileged if ( !defined($host) ); |
342 |
|
343 |
$Privileged ||= $User eq $Hosts->{$host}{user}; |
344 |
$Privileged ||= defined($Hosts->{$host}{moreUsers}{$User}); |
345 |
return $Privileged; |
346 |
} |
347 |
|
348 |
# |
349 |
# Returns the list of hosts that should appear in the navigation bar |
350 |
# for this user. If $getAll is set, the admin gets all the hosts. |
351 |
# Otherwise, regular users get hosts for which they are the user or |
352 |
# are listed in the moreUsers column in the hosts file. |
353 |
# |
354 |
sub GetUserHosts |
355 |
{ |
356 |
my($getAll) = @_; |
357 |
my @hosts; |
358 |
|
359 |
if ( $getAll && CheckPermission() ) { |
360 |
@hosts = sort keys %$Hosts; |
361 |
} else { |
362 |
@hosts = sort grep { $Hosts->{$_}{user} eq $User || |
363 |
defined($Hosts->{$_}{moreUsers}{$User}) } keys(%$Hosts); |
364 |
} |
365 |
return @hosts; |
366 |
} |
367 |
|
368 |
# |
369 |
# Given a host name tries to find the IP address. For non-dhcp hosts |
370 |
# we just return the host name. For dhcp hosts we check the address |
371 |
# the user is using ($ENV{REMOTE_ADDR}) and also the last-known IP |
372 |
# address for $host. (Later we should replace this with a broadcast |
373 |
# nmblookup.) |
374 |
# |
375 |
sub ConfirmIPAddress |
376 |
{ |
377 |
my($host) = @_; |
378 |
my $ipAddr = $host; |
379 |
|
380 |
if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp} |
381 |
&& $ENV{REMOTE_ADDR} =~ /^(\d+[\.\d]*)$/ ) { |
382 |
$ipAddr = $1; |
383 |
my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($ipAddr); |
384 |
if ( $netBiosHost ne $host ) { |
385 |
my($tryIP); |
386 |
GetStatusInfo("host(${EscURI($host)})"); |
387 |
if ( defined($StatusHost{dhcpHostIP}) |
388 |
&& $StatusHost{dhcpHostIP} ne $ipAddr ) { |
389 |
$tryIP = eval("qq{$Lang->{tryIP}}"); |
390 |
($netBiosHost, $netBiosUser) |
391 |
= $bpc->NetBiosInfoGet($StatusHost{dhcpHostIP}); |
392 |
} |
393 |
if ( $netBiosHost ne $host ) { |
394 |
ErrorExit(eval("qq{$Lang->{Can_t_find_IP_address_for}}"), |
395 |
eval("qq{$Lang->{host_is_a_DHCP_host}}")); |
396 |
} |
397 |
$ipAddr = $StatusHost{dhcpHostIP}; |
398 |
} |
399 |
} |
400 |
return $ipAddr; |
401 |
} |
402 |
|
403 |
########################################################################### |
404 |
# HTML layout subroutines |
405 |
########################################################################### |
406 |
|
407 |
sub Header |
408 |
{ |
409 |
my($title, $content, $noBrowse, $contentSub, $contentPost) = @_; |
410 |
my @adminLinks = ( |
411 |
{ link => "", name => $Lang->{Status}}, |
412 |
{ link => "?action=adminOpts", name => $Lang->{Admin_Options}, |
413 |
priv => 1}, |
414 |
{ link => "?action=summary", name => $Lang->{PC_Summary}}, |
415 |
{ link => "?action=view&type=LOG", name => $Lang->{LOG_file}, |
416 |
priv => 1}, |
417 |
{ link => "?action=LOGlist", name => $Lang->{Old_LOGs}, |
418 |
priv => 1}, |
419 |
{ link => "?action=emailSummary", name => $Lang->{Email_summary}, |
420 |
priv => 1}, |
421 |
{ link => "?action=view&type=config", name => $Lang->{Config_file}, |
422 |
priv => 1}, |
423 |
{ link => "?action=view&type=hosts", name => $Lang->{Hosts_file}, |
424 |
priv => 1}, |
425 |
{ link => "?action=queue", name => $Lang->{Current_queues}, |
426 |
priv => 1}, |
427 |
{ link => "?action=search", name => $Lang->{Search_archive}, |
428 |
priv => 0}, |
429 |
{ link => "?action=burn", name => $Lang->{Burn_media}, |
430 |
priv => 1}, |
431 |
@{$Conf{CgiNavBarLinks} || []}, |
432 |
); |
433 |
my $host = $In{host}; |
434 |
|
435 |
print $Cgi->header(); |
436 |
print <<EOF; |
437 |
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"> |
438 |
<html><head> |
439 |
<title>$title</title> |
440 |
<link rel=stylesheet type="text/css" href="$Conf{CgiImageDirURL}/$Conf{CgiCSSFile}" title="CSSFile"> |
441 |
$Conf{CgiHeaders} |
442 |
</head><body onLoad="document.getElementById('NavMenu').style.height=document.body.scrollHeight"> |
443 |
<a href="http://backuppc.sourceforge.net"><img src="$Conf{CgiImageDirURL}/logo.gif" hspace="5" vspace="7" border="0"></a><br> |
444 |
EOF |
445 |
|
446 |
if ( defined($Hosts) && defined($host) && defined($Hosts->{$host}) ) { |
447 |
print "<div class=\"NavMenu\">"; |
448 |
NavSectionTitle("${EscHTML($host)}"); |
449 |
print <<EOF; |
450 |
</div> |
451 |
<div class="NavMenu"> |
452 |
EOF |
453 |
NavLink("?host=${EscURI($host)}", |
454 |
"$host $Lang->{Home}", " class=\"navbar\""); |
455 |
NavLink("?action=browse&host=${EscURI($host)}", |
456 |
$Lang->{Browse}, " class=\"navbar\"") if ( !$noBrowse ); |
457 |
NavLink("?action=view&type=LOG&host=${EscURI($host)}", |
458 |
$Lang->{LOG_file}, " class=\"navbar\""); |
459 |
NavLink("?action=LOGlist&host=${EscURI($host)}", |
460 |
$Lang->{LOG_files}, " class=\"navbar\""); |
461 |
if ( -f "$TopDir/pc/$host/SmbLOG.bad" |
462 |
|| -f "$TopDir/pc/$host/SmbLOG.bad.z" |
463 |
|| -f "$TopDir/pc/$host/XferLOG.bad" |
464 |
|| -f "$TopDir/pc/$host/XferLOG.bad.z" ) { |
465 |
NavLink("?action=view&type=XferLOGbad&host=${EscURI($host)}", |
466 |
$Lang->{Last_bad_XferLOG}, " class=\"navbar\""); |
467 |
NavLink("?action=view&type=XferErrbad&host=${EscURI($host)}", |
468 |
$Lang->{Last_bad_XferLOG_errors_only}, |
469 |
" class=\"navbar\""); |
470 |
} |
471 |
if ( -f "$TopDir/pc/$host/config.pl" |
472 |
|| ($host ne "config" && -f "$TopDir/conf/$host.pl") ) { |
473 |
NavLink("?action=view&type=config&host=${EscURI($host)}", |
474 |
$Lang->{Config_file}, " class=\"navbar\""); |
475 |
} |
476 |
print "</div>\n"; |
477 |
} |
478 |
print("<div id=\"Content\">\n$content\n"); |
479 |
if ( defined($contentSub) && ref($contentSub) eq "CODE" ) { |
480 |
while ( (my $s = &$contentSub()) ne "" ) { |
481 |
print($s); |
482 |
} |
483 |
} |
484 |
print($contentPost) if ( defined($contentPost) ); |
485 |
print <<EOF; |
486 |
<br><br><br> |
487 |
</div> |
488 |
<div class="NavMenu" id="NavMenu" style="height:100%"> |
489 |
EOF |
490 |
my $hostSelectbox = "<option value=\"#\">$Lang->{Select_a_host}</option>"; |
491 |
my @hosts = GetUserHosts($Conf{CgiNavBarAdminAllHosts}); |
492 |
if ( defined($Hosts) && %$Hosts > 0 && @hosts ) { |
493 |
NavSectionTitle($Lang->{Hosts}); |
494 |
foreach my $host ( @hosts ) { |
495 |
NavLink("?host=${EscURI($host)}", $host) |
496 |
if ( @hosts < $Conf{CgiNavBarAdminAllHosts} ); |
497 |
my $sel = " selected" if ( $host eq $In{host} ); |
498 |
$hostSelectbox .= "<option value=\"?host=${EscURI($host)}\"$sel>" |
499 |
. "$host</option>"; |
500 |
} |
501 |
} |
502 |
if ( @hosts >= $Conf{CgiNavBarAdminAllHosts} ) { |
503 |
print <<EOF; |
504 |
<br> |
505 |
<select onChange="document.location=this.value"> |
506 |
$hostSelectbox |
507 |
</select> |
508 |
<br><br> |
509 |
EOF |
510 |
} |
511 |
if ( $Conf{CgiSearchBoxEnable} ) { |
512 |
print <<EOF; |
513 |
<form action="$MyURL" method="get"> |
514 |
<input type="text" name="host" size="14" maxlength="64"> |
515 |
<input type="hidden" name="action" value="hostInfo"><input type="submit" value="$Lang->{Go}" name="ignore"> |
516 |
</form> |
517 |
EOF |
518 |
} |
519 |
NavSectionTitle($Lang->{NavSectionTitle_}); |
520 |
foreach my $l ( @adminLinks ) { |
521 |
if ( $PrivAdmin || !$l->{priv} ) { |
522 |
my $txt = $l->{lname} ne "" ? $Lang->{$l->{lname}} : $l->{name}; |
523 |
NavLink($l->{link}, $txt); |
524 |
} |
525 |
} |
526 |
|
527 |
print <<EOF; |
528 |
<br><br><br> |
529 |
</div> |
530 |
EOF |
531 |
} |
532 |
|
533 |
sub Trailer |
534 |
{ |
535 |
print <<EOF; |
536 |
</body></html> |
537 |
EOF |
538 |
} |
539 |
|
540 |
|
541 |
sub NavSectionTitle |
542 |
{ |
543 |
my($head) = @_; |
544 |
print <<EOF; |
545 |
<div class="NavTitle">$head</div> |
546 |
EOF |
547 |
} |
548 |
|
549 |
sub NavSectionStart |
550 |
{ |
551 |
} |
552 |
|
553 |
sub NavSectionEnd |
554 |
{ |
555 |
} |
556 |
|
557 |
sub NavLink |
558 |
{ |
559 |
my($link, $text) = @_; |
560 |
if ( defined($link) ) { |
561 |
my($class); |
562 |
$class = " class=\"NavCurrent\"" |
563 |
if ( length($link) && $ENV{REQUEST_URI} =~ /\Q$link\E$/ |
564 |
|| $link eq "" && $ENV{REQUEST_URI} !~ /\?/ ); |
565 |
$link = "$MyURL$link" if ( $link eq "" || $link =~ /^\?/ ); |
566 |
print <<EOF; |
567 |
<a href="$link"$class>$text</a> |
568 |
EOF |
569 |
} else { |
570 |
print <<EOF; |
571 |
$text<br> |
572 |
EOF |
573 |
} |
574 |
} |
575 |
|
576 |
sub h1 |
577 |
{ |
578 |
my($str) = @_; |
579 |
return \<<EOF; |
580 |
<div class="h1">$str</div> |
581 |
EOF |
582 |
} |
583 |
|
584 |
sub h2 |
585 |
{ |
586 |
my($str) = @_; |
587 |
return \<<EOF; |
588 |
<div class="h2">$str</div> |
589 |
EOF |
590 |
} |