[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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-2007 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 3.1.0, released 25 Nov 2007. 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 $LogDir $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 use vars qw($connexionDb $mysqlServerUsername $mysqlServerPw $cookie 54 $peopleDn $ldap_server $adminDn $adminPw $droitsDn $cn @cn 55 $baseurl); 56 57 @ISA = qw(Exporter); 58 59 @EXPORT = qw( ); 60 61 @EXPORT_OK = qw( 62 timeStamp2 63 HostLink 64 UserLink 65 EscHTML 66 EscURI 67 ErrorExit 68 ServerConnect 69 GetStatusInfo 70 ReadUserEmailInfo 71 CheckPermission 72 GetUserHosts 73 ConfirmIPAddress 74 Header 75 Trailer 76 NavSectionTitle 77 NavSectionStart 78 NavSectionEnd 79 NavLink 80 h1 81 h2 82 $Cgi %In $MyURL $User %Conf $TopDir $LogDir $BinDir $bpc 83 %Status %Info %Jobs @BgQueue @UserQueue @CmdQueue 84 %QueueLen %StatusHost 85 $Hosts $HostsMTime $ConfigMTime $PrivAdmin 86 %UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq 87 $Lang 88 ); 89 90 %EXPORT_TAGS = ( 91 'all' => [ @EXPORT_OK ], 92 ); 93 94 sub NewRequest 95 { 96 $Cgi = new CGI; 97 %In = $Cgi->Vars; 98 99 if ( !defined($bpc) ) { 100 ErrorExit($Lang->{BackupPC__Lib__new_failed__check_apache_error_log}) 101 if ( !($bpc = BackupPC::Lib->new(undef, undef, undef, 1)) ); 102 $TopDir = $bpc->TopDir(); 103 $LogDir = $bpc->LogDir(); 104 $BinDir = $bpc->BinDir(); 105 %Conf = $bpc->Conf(); 106 $Lang = $bpc->Lang(); 107 $ConfigMTime = $bpc->ConfigMTime(); 108 umask($Conf{UmaskMode}); 109 } elsif ( $bpc->ConfigMTime() != $ConfigMTime ) { 110 $bpc->ConfigRead(); 111 $TopDir = $bpc->TopDir(); 112 $LogDir = $bpc->LogDir(); 113 $BinDir = $bpc->BinDir(); 114 %Conf = $bpc->Conf(); 115 $Lang = $bpc->Lang(); 116 $ConfigMTime = $bpc->ConfigMTime(); 117 umask($Conf{UmaskMode}); 118 } 119 120 # 121 # Default REMOTE_USER so in a miminal installation the user 122 # has a sensible default. 123 # 124 #$ENV{REMOTE_USER} = $Conf{BackupPCUser} if ( $ENV{REMOTE_USER} eq "" ); 125 126 # 127 # We require that Apache pass in $ENV{SCRIPT_NAME} and $ENV{REMOTE_USER}. 128 # The latter requires .ht_access style authentication. Replace this 129 # code if you are using some other type of authentication, and have 130 # a different way of getting the user name. 131 # 132 #$MyURL = $ENV{SCRIPT_NAME}; 133 #$User = "www-se3"; 134 135 use CGI::Cookie; 136 use Net::LDAP; 137 require '/etc/SeConfig.ph'; 138 # Identification de l'utilisateur 139 # =============================== 140 # R\xe9cup\xe9ration du cookie 141 my %cookies = fetch CGI::Cookie; 142 #open LOG, '>/tmp/bpc.log'; 143 #foreach $cookie (keys %cookies) { 144 # print LOG "$cookie : $cookies{$cookie}\n"; 145 #} 146 #close LOG; 147 my $isAdmin = "N"; 148 if ($cookies{'SambaEdu3'}) { 149 my $session = $cookies{'SambaEdu3'}->value; 150 # Connexion MySql 151 my $lcs_db = DBI->connect("DBI:mysql:$connexionDb", $mysqlServerUsername, $mysqlServerPw); 152 my $requete = $lcs_db->prepare("select id, login from sessions where (sess = '$session')"); 153 $requete->execute(); 154 my ( $id, $login ) = $requete->fetchrow_array; 155 $lcs_db->disconnect; 156 # Validation 157 my $admindn = 'uid=' . $login .",". $peopleDn; 158 my @attrs = ('cn'); 159 my $lcs_ldap = Net::LDAP->new("$ldap_server"); 160 $lcs_ldap->bind(dn => "$adminDn", 161 password => "$adminPw"); 162 my $res = $lcs_ldap->search(base => "cn=system_is_admin,$droitsDn", 163 scope => 'subtree', 164 attrs => \@attrs, 165 filter => "(member=$admindn)"); 166 foreach my $entry ($res->entries) { 167 @cn = $entry->get('cn'); 168 } 169 if ($cn[0] eq 'system_is_admin') { 170 $isAdmin = "Y"; 171 } 172 my $res = $lcs_ldap->search(base => "cn=se3_is_admin,$droitsDn", 173 scope => 'subtree', 174 attrs => \@attrs, 175 filter => "(member=$admindn)"); 176 foreach my $entry ($res->entries) { 177 @cn = $entry->get('cn'); 178 } 179 if ($cn[0] eq 'se3_is_admin') { 180 $isAdmin = "Y"; 181 } 182 $lcs_ldap->unbind(); 183 } 184 185 if ($isAdmin eq 'N') { 186 print $Cgi->header(); 187 print 188 "<!doctype html public \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n", 189 "<html><head>\n", 190 "<meta http-equiv=\"Refresh\" content=\"0; url=" . $baseurl, 191 "Err/401.html\">\n", 192 "</head></html>\n"; 193 exit; 194 } else { 195 $User = 'backuppc'; 196 } 197 198 # 199 # Handle LDAP uid=user when using mod_authz_ldap and otherwise untaint 200 # 201 $User = $1 if ( $User =~ /uid=([^,]+)/i || $User =~ /(.*)/ ); 202 203 # 204 # Clean up %ENV for taint checking 205 # 206 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; 207 $ENV{PATH} = $Conf{MyPath}; 208 209 # 210 # Verify we are running as the correct user 211 # 212 if ( $Conf{BackupPCUserVerify} 213 && $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { 214 ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}"), <<EOF); 215 This script needs to run as the user specified in \$Conf{BackupPCUser}, 216 which is set to $Conf{BackupPCUser}. 217 <p> 218 This is an installation problem. If you are using mod_perl then 219 it appears that Apache is not running as user $Conf{BackupPCUser}. 220 If you are not using mod_perl, then most like setuid is not working 221 properly on BackupPC_Admin. Check the permissions on 222 $Conf{CgiDir}/BackupPC_Admin and look at the documentation. 223 EOF 224 } 225 226 if ( !defined($Hosts) || $bpc->HostsMTime() != $HostsMTime ) { 227 $HostsMTime = $bpc->HostsMTime(); 228 $Hosts = $bpc->HostInfoRead(); 229 230 # turn moreUsers list into a hash for quick lookups 231 foreach my $host (keys %$Hosts) { 232 $Hosts->{$host}{moreUsers} = 233 {map {$_, 1} split(",", $Hosts->{$host}{moreUsers}) } 234 } 235 } 236 237 # 238 # Untaint the host name 239 # 240 if ( $In{host} =~ /^([\w.\s-]+)$/ ) { 241 $In{host} = $1; 242 } else { 243 delete($In{host}); 244 } 245 } 246 247 sub timeStamp2 248 { 249 my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) 250 = localtime($_[0] == 0 ? time : $_[0] ); 251 $mon++; 252 if ( $Conf{CgiDateFormatMMDD} == 2 ) { 253 $year += 1900; 254 return sprintf("%04d-%02d-%02d %02d:%02d", $year, $mon, $mday, $hour, $min); 255 } elsif ( $Conf{CgiDateFormatMMDD} ) { 256 return sprintf("$mon/$mday %02d:%02d", $hour, $min); 257 } else { 258 return sprintf("$mday/$mon %02d:%02d", $hour, $min); 259 } 260 } 261 262 sub HostLink 263 { 264 my($host) = @_; 265 my($s); 266 if ( defined($Hosts->{$host}) || defined($Status{$host}) ) { 267 $s = "<a href=\"$MyURL?host=$EscURI($host)}\">$host</a>"; 268 } else { 269 $s = $host; 270 } 271 return \$s; 272 } 273 274 sub UserLink 275 { 276 my($user) = @_; 277 my($s); 278 279 return \$user if ( $user eq "" 280 || $Conf{CgiUserUrlCreate} eq "" ); 281 if ( $Conf{CgiUserHomePageCheck} eq "" 282 || -f sprintf($Conf{CgiUserHomePageCheck}, $user, $user, $user) ) { 283 $s = "<a href=\"" 284 . sprintf($Conf{CgiUserUrlCreate}, $user, $user, $user) 285 . "\">$user</a>"; 286 } else { 287 $s = $user; 288 } 289 return \$s; 290 } 291 292 sub EscHTML 293 { 294 my($s) = @_; 295 $s =~ s/&/&/g; 296 $s =~ s/\"/"/g; 297 $s =~ s/>/>/g; 298 $s =~ s/</</g; 299 ### $s =~ s{([^[:print:]])}{sprintf("&\#x%02X;", ord($1));}eg; 300 return \$s; 301 } 302 303 sub EscURI 304 { 305 my($s) = @_; 306 $s =~ s{([^\w.\/-])}{sprintf("%%%02X", ord($1));}eg; 307 return \$s; 308 } 309 310 sub ErrorExit 311 { 312 my(@mesg) = @_; 313 my($head) = shift(@mesg); 314 my($mesg) = join("</p>\n<p>", @mesg); 315 316 if ( !defined($ENV{REMOTE_USER}) ) { 317 $mesg .= <<EOF; 318 <p> 319 Note: \$ENV{REMOTE_USER} is not set, which could mean there is an 320 installation problem. BackupPC_Admin expects Apache to authenticate 321 the user and pass their user name into this script as the REMOTE_USER 322 environment variable. See the documentation. 323 EOF 324 } 325 326 $bpc->ServerMesg("log User $User (host=$In{host}) got CGI error: $head") 327 if ( defined($bpc) ); 328 if ( !defined($Lang->{Error}) ) { 329 $mesg = <<EOF if ( !defined($mesg) ); 330 There is some problem with the BackupPC installation. 331 Please check the permissions on BackupPC_Admin. 332 EOF 333 my $content = <<EOF; 334 $h1("Error: Unable to read config.pl or language strings!!")} 335 <p>$mesg</p> 336 EOF 337 Header("BackupPC: Error", $content); 338 Trailer(); 339 } else { 340 my $content = eval("qq{$Lang->{Error____head}}"); 341 Header(eval("qq{$Lang->{Error}}"), $content); 342 Trailer(); 343 } 344 exit(1); 345 } 346 347 sub ServerConnect 348 { 349 # 350 # Verify that the server connection is ok 351 # 352 return if ( $bpc->ServerOK() ); 353 $bpc->ServerDisconnect(); 354 if ( my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ) { 355 if ( CheckPermission() 356 && -f $Conf{ServerInitdPath} 357 && $Conf{ServerInitdStartCmd} ne "" ) { 358 my $content = eval("qq{$Lang->{Admin_Start_Server}}"); 359 Header(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}"), $content); 360 Trailer(); 361 exit(1); 362 } else { 363 ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); 364 } 365 } 366 } 367 368 sub GetStatusInfo 369 { 370 my($status) = @_; 371 ServerConnect(); 372 %Status = () if ( $status =~ /\bhosts\b/ ); 373 %StatusHost = () if ( $status =~ /\bhost\(/ ); 374 my $reply = $bpc->ServerMesg("status $status"); 375 $reply = $1 if ( $reply =~ /(.*)/s ); 376 eval($reply); 377 # ignore status related to admin and trashClean jobs 378 if ( $status =~ /\bhosts\b/ ) { 379 foreach my $host ( grep(/admin/, keys(%Status)) ) { 380 delete($Status{$host}) if ( $bpc->isAdminJob($host) ); 381 } 382 delete($Status{$bpc->trashJob}); 383 } 384 } 385 386 sub ReadUserEmailInfo 387 { 388 if ( (stat("$LogDir/UserEmailInfo.pl"))[9] != $UserEmailInfoMTime ) { 389 do "$LogDir/UserEmailInfo.pl"; 390 $UserEmailInfoMTime = (stat("$LogDir/UserEmailInfo.pl"))[9]; 391 } 392 } 393 394 # 395 # Check if the user is privileged. A privileged user can access 396 # any information (backup files, logs, status pages etc). 397 # 398 # A user is privileged if they belong to the group 399 # $Conf{CgiAdminUserGroup}, or they are in $Conf{CgiAdminUsers} 400 # or they are the user assigned to a host in the host file. 401 # 402 sub CheckPermission 403 { 404 my($host) = @_; 405 my $Privileged = 0; 406 407 return 0 if ( $User eq "" && $Conf{CgiAdminUsers} ne "*" 408 || $host ne "" && !defined($Hosts->{$host}) ); 409 if ( $Conf{CgiAdminUserGroup} ne "" ) { 410 my($n,$p,$gid,$mem) = getgrnam($Conf{CgiAdminUserGroup}); 411 $Privileged ||= ($mem =~ /\b\Q$User\E\b/); 412 } 413 if ( $Conf{CgiAdminUsers} ne "" ) { 414 $Privileged ||= ($Conf{CgiAdminUsers} =~ /\b\Q$User\E\b/); 415 $Privileged ||= $Conf{CgiAdminUsers} eq "*"; 416 } 417 $PrivAdmin = $Privileged; 418 return $Privileged if ( !defined($host) ); 419 420 $Privileged ||= $User eq $Hosts->{$host}{user}; 421 $Privileged ||= defined($Hosts->{$host}{moreUsers}{$User}); 422 return $Privileged; 423 } 424 425 # 426 # Returns the list of hosts that should appear in the navigation bar 427 # for this user. If $getAll is set, the admin gets all the hosts. 428 # Otherwise, regular users get hosts for which they are the user or 429 # are listed in the moreUsers column in the hosts file. 430 # 431 sub GetUserHosts 432 { 433 my($getAll) = @_; 434 my @hosts; 435 436 if ( $getAll && CheckPermission() ) { 437 @hosts = sort keys %$Hosts; 438 } else { 439 @hosts = sort grep { $Hosts->{$_}{user} eq $User || 440 defined($Hosts->{$_}{moreUsers}{$User}) } keys(%$Hosts); 441 } 442 return @hosts; 443 } 444 445 # 446 # Given a host name tries to find the IP address. For non-dhcp hosts 447 # we just return the host name. For dhcp hosts we check the address 448 # the user is using ($ENV{REMOTE_ADDR}) and also the last-known IP 449 # address for $host. (Later we should replace this with a broadcast 450 # nmblookup.) 451 # 452 sub ConfirmIPAddress 453 { 454 my($host) = @_; 455 my $ipAddr = $host; 456 457 if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp} 458 && $ENV{REMOTE_ADDR} =~ /^(\d+[\.\d]*)$/ ) { 459 $ipAddr = $1; 460 my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($ipAddr); 461 if ( $netBiosHost ne $host ) { 462 my($tryIP); 463 GetStatusInfo("host($EscURI($host)})"); 464 if ( defined($StatusHost{dhcpHostIP}) 465 && $StatusHost{dhcpHostIP} ne $ipAddr ) { 466 $tryIP = eval("qq{$Lang->{tryIP}}"); 467 ($netBiosHost, $netBiosUser) 468 = $bpc->NetBiosInfoGet($StatusHost{dhcpHostIP}); 469 } 470 if ( $netBiosHost ne $host ) { 471 ErrorExit(eval("qq{$Lang->{Can_t_find_IP_address_for}}"), 472 eval("qq{$Lang->{host_is_a_DHCP_host}}")); 473 } 474 $ipAddr = $StatusHost{dhcpHostIP}; 475 } 476 } 477 return $ipAddr; 478 } 479 480 ########################################################################### 481 # HTML layout subroutines 482 ########################################################################### 483 484 sub Header 485 { 486 my($title, $content, $noBrowse, $contentSub, $contentPost) = @_; 487 my @adminLinks = ( 488 { link => "", name => $Lang->{Status}}, 489 { link => "?action=summary", name => $Lang->{PC_Summary}}, 490 { link => "?action=editConfig", name => $Lang->{CfgEdit_Edit_Config}, 491 priv => 1}, 492 { link => "?action=editConfig&newMenu=hosts", 493 name => $Lang->{CfgEdit_Edit_Hosts}, 494 priv => 1}, 495 { link => "?action=adminOpts", name => $Lang->{Admin_Options}, 496 priv => 1}, 497 { link => "?action=view&type=LOG", name => $Lang->{LOG_file}, 498 priv => 1}, 499 { link => "?action=LOGlist", name => $Lang->{Old_LOGs}, 500 priv => 1}, 501 { link => "?action=emailSummary", name => $Lang->{Email_summary}, 502 priv => 1}, 503 { link => "?action=queue", name => $Lang->{Current_queues}, 504 priv => 1}, 505 @{$Conf{CgiNavBarLinks} || []}, 506 ); 507 my $host = $In{host}; 508 509 binmode(STDOUT, ":utf8"); 510 print $Cgi->header(-charset => "utf-8"); 511 print <<EOF; 512 <!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"> 513 <html><head> 514 <title>$title</title> 515 <link rel=stylesheet type="text/css" href="$Conf{CgiImageDirURL}/$Conf{CgiCSSFile}" title="CSSFile"> 516 $Conf{CgiHeaders} 517 <script src="$Conf{CgiImageDirURL}/sorttable.js"></script> 518 </head><body onLoad="document.getElementById('NavMenu').style.height=document.body.scrollHeight"> 519 <a href="http://backuppc.sourceforge.net"><img src="$Conf{CgiImageDirURL}/logo.gif" hspace="5" vspace="7" border="0"></a><br> 520 EOF 521 522 if ( defined($Hosts) && defined($host) && defined($Hosts->{$host}) ) { 523 print "<div class=\"NavMenu\">"; 524 NavSectionTitle("$EscHTML($host)}"); 525 print <<EOF; 526 </div> 527 <div class="NavMenu"> 528 EOF 529 NavLink("?host=$EscURI($host)}", 530 "$host $Lang->{Home}", " class=\"navbar\""); 531 NavLink("?action=browse&host=$EscURI($host)}", 532 $Lang->{Browse}, " class=\"navbar\"") if ( !$noBrowse ); 533 NavLink("?action=view&type=LOG&host=$EscURI($host)}", 534 $Lang->{LOG_file}, " class=\"navbar\""); 535 NavLink("?action=LOGlist&host=$EscURI($host)}", 536 $Lang->{LOG_files}, " class=\"navbar\""); 537 if ( -f "$TopDir/pc/$host/SmbLOG.bad" 538 || -f "$TopDir/pc/$host/SmbLOG.bad.z" 539 || -f "$TopDir/pc/$host/XferLOG.bad" 540 || -f "$TopDir/pc/$host/XferLOG.bad.z" ) { 541 NavLink("?action=view&type=XferLOGbad&host=$EscURI($host)}", 542 $Lang->{Last_bad_XferLOG}, " class=\"navbar\""); 543 NavLink("?action=view&type=XferErrbad&host=$EscURI($host)}", 544 $Lang->{Last_bad_XferLOG_errors_only}, 545 " class=\"navbar\""); 546 } 547 if ( $Conf{CgiUserConfigEditEnable} || $PrivAdmin ) { 548 NavLink("?action=editConfig&host=$EscURI($host)}", 549 $Lang->{CfgEdit_Edit_Config}, " class=\"navbar\""); 550 } elsif ( -f "$TopDir/pc/$host/config.pl" 551 || ($host ne "config" && -f "/etc/backuppc/$host.pl") ) { 552 NavLink("?action=view&type=config&host=$EscURI($host)}", 553 $Lang->{Config_file}, " class=\"navbar\""); 554 } 555 print "</div>\n"; 556 } 557 print("<div id=\"Content\">\n$content\n"); 558 if ( defined($contentSub) && ref($contentSub) eq "CODE" ) { 559 while ( (my $s = &$contentSub()) ne "" ) { 560 print($s); 561 } 562 } 563 print($contentPost) if ( defined($contentPost) ); 564 print <<EOF; 565 <br><br><br> 566 </div> 567 <div class="NavMenu" id="NavMenu" style="height:100%"> 568 EOF 569 my $hostSelectbox = "<option value=\"#\">$Lang->{Select_a_host}</option>"; 570 my @hosts = GetUserHosts($Conf{CgiNavBarAdminAllHosts}); 571 NavSectionTitle($Lang->{Hosts}); 572 if ( defined($Hosts) && %$Hosts > 0 && @hosts ) { 573 foreach my $host ( @hosts ) { 574 NavLink("?host=$EscURI($host)}", $host) 575 if ( @hosts < $Conf{CgiNavBarAdminAllHosts} ); 576 my $sel = " selected" if ( $host eq $In{host} ); 577 $hostSelectbox .= "<option value=\"?host=$EscURI($host)}\"$sel>" 578 . "$host</option>"; 579 } 580 } 581 if ( @hosts >= $Conf{CgiNavBarAdminAllHosts} ) { 582 print <<EOF; 583 <br> 584 <select onChange="document.location=this.value"> 585 $hostSelectbox 586 </select> 587 <br><br> 588 EOF 589 } 590 if ( $Conf{CgiSearchBoxEnable} ) { 591 print <<EOF; 592 <form action="$MyURL" method="get"> 593 <input type="text" name="host" size="14" maxlength="64"> 594 <input type="hidden" name="action" value="hostInfo"><input type="submit" value="$Lang->{Go}" name="ignore"> 595 </form> 596 EOF 597 } 598 NavSectionTitle($Lang->{NavSectionTitle_}); 599 foreach my $l ( @adminLinks ) { 600 if ( $PrivAdmin || !$l->{priv} ) { 601 my $txt = $l->{lname} ne "" ? $Lang->{$l->{lname}} : $l->{name}; 602 NavLink($l->{link}, $txt); 603 } 604 } 605 606 print <<EOF; 607 <br><br><br> 608 </div> 609 EOF 610 } 611 612 sub Trailer 613 { 614 print <<EOF; 615 </body></html> 616 EOF 617 } 618 619 620 sub NavSectionTitle 621 { 622 my($head) = @_; 623 print <<EOF; 624 <div class="NavTitle">$head</div> 625 EOF 626 } 627 628 sub NavSectionStart 629 { 630 } 631 632 sub NavSectionEnd 633 { 634 } 635 636 sub NavLink 637 { 638 my($link, $text) = @_; 639 if ( defined($link) ) { 640 my($class); 641 $class = " class=\"NavCurrent\"" 642 if ( length($link) && $ENV{REQUEST_URI} =~ /\Q$link\E$/ 643 || $link eq "" && $ENV{REQUEST_URI} !~ /\?/ ); 644 $link = "$MyURL$link" if ( $link eq "" || $link =~ /^\?/ ); 645 print <<EOF; 646 <a href="$link"$class>$text</a> 647 EOF 648 } else { 649 print <<EOF; 650 $text<br> 651 EOF 652 } 653 } 654 655 sub h1 656 { 657 my($str) = @_; 658 return \<<EOF; 659 <div class="h1">$str</div> 660 EOF 661 } 662 663 sub h2 664 { 665 my($str) = @_; 666 return \<<EOF; 667 <div class="h2">$str</div> 668 EOF 669 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |