/[BackupPC]/trunk/lib/BackupPC/CGI/Lib.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/BackupPC/CGI/Lib.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 231 - (show annotations)
Fri Oct 28 12:41:47 2005 UTC (18 years, 6 months ago) by dpavlin
File size: 17073 byte(s)
local change: added check of $Conf{CgiAdminUserGroup} against
REMOTE_NTGROUP from mod_ntlm to support single-sign on

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

  ViewVC Help
Powered by ViewVC 1.1.26