source: trunk/dns.cgi@ 117

Last change on this file since 117 was 117, checked in by Kris Deugau, 13 years ago

/trunk

Add getParents (untested) and isParent (tested) subs
Add some supporting hashes for entity -> parent(entity)

relationships in the database - private to DNSDB.pm

Rename tmp_ruri to uri_self for clarity and reuse
Move uri_self munging from ##common area so that more

subs can use it

Update group tree to change the current group by clicking

the group name. Working comments need to be cleaned up
and choose-a-group dropdown removed from the menu

  • Property svn:executable set to *
  • Property svn:keywords set to Date Rev Author Id
File size: 67.7 KB
Line 
1#!/usr/bin/perl -w -T
2# dns/cgi-bin/dns.cgi
3###
4# SVN revision info
5# $Date: 2011-09-01 19:55:00 +0000 (Thu, 01 Sep 2011) $
6# SVN revision $Rev: 117 $
7# Last update by $Author: kdeugau $
8###
9# Copyright (C) 2008-2011 - Kris Deugau <kdeugau@deepnet.cx>
10
11use strict;
12use warnings;
13
14use CGI::Carp qw (fatalsToBrowser);
15use CGI::Simple;
16use HTML::Template;
17use CGI::Session;
18use Crypt::PasswdMD5;
19use Digest::MD5 qw(md5_hex);
20use Net::DNS;
21use DBI;
22use Data::Dumper;
23
24#sub is_tainted {
25# # from perldoc perlsec
26# return ! eval { eval("#" . substr(join("", @_), 0, 0)); 1 };
27#}
28#use Cwd 'abs_path';
29#use File::Basename;
30#use lib dirname( abs_path $0 );
31#die "argh! tainted!" if is_tainted($0);
32#die "argh! \@INC got tainted!" if is_tainted(@INC);
33
34# custom modules
35use lib '.';
36use DNSDB qw(:ALL);
37
38my @debugbits; # temp, to be spit out near the end of processing
39my $debugenv = 1;
40
41# Let's do these templates right...
42my $templatedir = "templates";
43my $sessiondir = "session";
44
45# Set up the CGI object...
46my $q = new CGI::Simple;
47# ... and get query-string params as well as POST params if necessary
48$q->parse_query_string;
49
50# This is probably excessive fiddling, but it puts the parameters somewhere my fingers know about...
51my %webvar = $q->Vars;
52
53# persistent stuff needed on most/all pages
54my $sid = ($webvar{sid} ? $webvar{sid} : undef);
55my $session = new CGI::Session("driver:File", $sid, {Directory => $sessiondir})
56 or die CGI::Session->errstr();
57#$sid = $session->id() if !$sid;
58if (!$sid) {
59 # init stuff. can probably axe this down to just above if'n'when user manipulation happens
60 $sid = $session->id();
61# need to know the "upper" group the user can deal with; may as well
62# stick this in the session rather than calling out to the DB every time.
63 $session->param('logingroup',1);
64 $session->param('curgroup',1); # yes, we *do* need to track this too. er, probably.
65 $session->param('domlistsortby','domain');
66 $session->param('domlistorder','ASC');
67 $session->param('useradminsortby','user');
68 $session->param('useradminorder','ASC');
69 $session->param('grpmansortby','group');
70 $session->param('grpmanorder','ASC');
71 $session->param('reclistsortby','host');
72 $session->param('reclistorder','ASC');
73# $session->param('filter','login');
74# $session->param('startwith','login');
75# $session->param('searchsubs','login');
76}
77
78my $logingroup = ($session->param('logingroup') ? $session->param('logingroup') : 1);
79my $curgroup = ($session->param('curgroup') ? $session->param('curgroup') : $logingroup);
80my $group = ($webvar{group} ? $webvar{group} : 1);
81
82# per-page startwith, filter, searchsubs
83$session->param($webvar{page}.'startwith', $webvar{startwith}) if defined($webvar{startwith});
84$session->param($webvar{page}.'filter', $webvar{filter}) if defined($webvar{filter});
85$webvar{searchsubs} =~ s/^n ?// if $webvar{searchsubs};
86$session->param($webvar{page}.'searchsubs', $webvar{searchsubs}) if defined($webvar{searchsubs});
87
88# decide which page to spit out...
89# also set $webvar{page} before we try to use it.
90$webvar{page} = 'login' if !$webvar{page};
91
92my $startwith = $session->param($webvar{page}.'startwith');
93my $filter = $session->param($webvar{page}.'filter');
94my $searchsubs = $session->param($webvar{page}.'searchsubs');
95
96# nrgh, can't handle login here because we don't have a database handle to check the user/pass with yet
97
98my $header = HTML::Template->new(filename => "$templatedir/header.tmpl");
99my $footer = HTML::Template->new(filename => "$templatedir/footer.tmpl");
100
101## set up "URL to self"
102# @#$%@%@#% XHTML - & in a URL must be escaped. >:(
103my $uri_self = $ENV{REQUEST_URI};
104$uri_self =~ s/\&([a-z])/\&amp\;$1/g;
105
106# le sigh. and we need to strip any previous action
107$uri_self =~ s/\&amp;action=[^&]+//g;
108
109# and search filter options. these get stored in the session, but discarded
110# as soon as you switch to a different page.
111##fixme: think about retaining these on a per-page basis, as well as offset; same as the sort-order bits
112no warnings qw(uninitialized);
113$uri_self =~ s/\&amp;startwith=[a-z09-]*(\&)?/$1/g;
114$uri_self =~ s/\&amp;searchsubs=[a-z09-]*(\&)?/$1/g;
115$uri_self =~ s/\&amp;filter=[a-z09-]*(\&)?/$1/g;
116use warnings qw(uninitialized);
117
118# default
119#my $perpage = 15;
120my $perpage = 5;
121my $offset = ($webvar{offset} ? $webvar{offset} : 0);
122
123# NB: these must match the field name and SQL ascend/descend syntax respectively
124my $sortby = "domain";
125my $sortorder = "ASC";
126
127#my ($dbh,$msg) = connectDB("dnsdb","dnsdb","secret","newdbhost");
128# dbname, user, pass, host (optional)
129my ($dbh,$msg) = connectDB("dnsdb", "dnsdb", "secret", "dnsdbhost");
130#my $dbh = DBI->connect("DBI:mysql:database=vegadns","vegadns","secret",
131# { AutoCommit => 0 }) or die $DBI::errstr;
132
133##fixme. PLEASE! <G>
134print $msg if !$dbh;
135
136# fiddle hardcoded "defaults" as per system/user (?) prefs
137initGlobals($dbh);
138
139# handle login redirect
140if ($webvar{action}) {
141 if ($webvar{action} eq 'login') {
142 # Snag ACL/permissions here too
143 my $sth = $dbh->prepare("SELECT user_id,group_id,password,firstname,lastname FROM users WHERE username=?");
144 $sth->execute($webvar{username});
145 my ($uid,$gid,$pass,$fname,$lname) = $sth->fetchrow_array;
146 $webvar{loginfailed} = 1 if !defined($uid);
147
148 if ($pass =~ m|^\$1\$([A-Za-z0-9/.]+)\$|) {
149 # native passwords (crypt-md5)
150 $webvar{loginfailed} = 1 if $pass ne unix_md5_crypt($webvar{password},$1);
151 } elsif ($pass =~ /^[0-9a-f]{32}$/) {
152 # VegaDNS import (hex-coded MD5)
153 $webvar{loginfailed} = 1 if $pass ne md5_hex($webvar{password});
154 } else {
155 # plaintext (convenient now and then)
156 $webvar{loginfailed} = 1 if $pass ne $webvar{password};
157 }
158
159 # set session bits
160 $session->param('logingroup',$gid);
161 $session->param('curgroup',$gid);
162 $session->param('uid',$uid);
163 $session->param('username',$webvar{username});
164
165 changepage(page => "domlist") if !defined($webvar{loginfailed});
166 } elsif ($webvar{action} eq 'logout') {
167 # delete the session
168 $session->delete();
169 $session->flush();
170
171 my $newurl = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}";
172 $newurl =~ s|/[^/]+$|/|;
173 print "Status: 302\nLocation: $newurl\n\n";
174 exit;
175
176 } elsif ($webvar{action} eq 'chgroup') {
177 # fiddle session-stored group data
178 # magic incantation to... uhhh...
179
180 # ... and the "change group" bits...
181 $uri_self =~ s/\&amp;group=[^&]*//g;
182
183 $session->param('curgroup', $webvar{group});
184 $curgroup = ($webvar{group} ? $webvar{group} : $session->param('curgroup'));
185 }
186} # handle global webvar{action}s
187
188initPermissions($dbh,$session->param('uid'));
189
190## Default page is a login page
191#my $page; # to be initialized as an HTML::Template entity sooner or later
192
193
194#if (!$webvar{page}) {
195# $page = HTML::Template->new(filename => "$templatedir/login.tmpl");
196#} else {
197#}
198
199my $page = HTML::Template->new(filename => "$templatedir/$webvar{page}.tmpl");
200
201$page->param(sid => $sid);
202
203if ($webvar{page} eq 'login') {
204
205 $page->param(loginfailed => 1) if $webvar{loginfailed};
206##fixme: set up session init to actually *check* for session timeout
207 $page->param(timeout => 1) if $webvar{sesstimeout};
208
209} elsif ($webvar{page} eq 'domlist' or $webvar{page} eq 'index') {
210
211# hmm. seeing problems in some possibly-not-so-corner cases.
212# this currently only handles "domain on", "domain off"
213 if (defined($webvar{action})) {
214 my $stat = domStatus($dbh,$webvar{id},$webvar{action});
215 logaction($webvar{id}, $session->param("username"), parentID($webvar{id}, 'dom', 'group'),
216 "Changed ".domainName($dbh, $webvar{id})." state to ".($stat ? 'active' : 'inactive'));
217 }
218
219 $page->param(curpage => $webvar{page});
220 $page->param(errmsg => $webvar{errmsg}) if $webvar{errmsg};
221# if ($webvar{del_failed}) {
222# $page->param(del_failed => 1);
223# $page->param(errmsg => $webvar{errmsg});
224# }
225
226 listdomains();
227
228} elsif ($webvar{page} eq 'newdomain') {
229
230 changepage(page => "domlist", errmsg => "You are not permitted to add domains")
231 unless ($permissions{admin} || $permissions{domain_create});
232
233 # hmm. nothing to do here?
234 # - group list is filled by the same bit that fills the group list in the menu
235 if ($webvar{add_failed}) {
236 $page->param(add_failed => 1);
237 $page->param(errmsg => $webvar{errmsg});
238 $page->param(domain => $webvar{domain});
239 }
240
241} elsif ($webvar{page} eq 'adddomain') {
242
243 changepage(page => "domlist", errmsg => "You are not permitted to add domains")
244 unless ($permissions{admin} || $permissions{domain_create});
245
246 my ($code,$msg) = addDomain($dbh,$webvar{domain},$webvar{group},($webvar{makeactive} eq 'on' ? 1 : 0));
247
248 if ($code eq 'OK') {
249 logaction($msg, $session->param("username"), $webvar{group}, "Added domain $webvar{domain}");
250 changepage(page => "reclist", id => $msg);
251 } else {
252 logaction(0, $session->param("username"), $webvar{group}, "Failed adding domain $webvar{domain} ($msg)");
253 changepage(page => "newdomain", add_failed => 1, domain => $webvar{domain}, errmsg => $msg);
254 }
255
256} elsif ($webvar{page} eq 'deldom') {
257
258 changepage(page => "domlist", errmsg => "You are not permitted to delete domains")
259 unless ($permissions{admin} || $permissions{domain_delete});
260
261 $page->param(id => $webvar{id});
262
263 # first pass = confirm y/n (sorta)
264 if (!defined($webvar{del})) {
265
266 $page->param(del_getconf => 1);
267 $page->param(domain => domainName($dbh,$webvar{id}));
268
269 } elsif ($webvar{del} eq 'ok') {
270
271 my $pargroup = parentID($webvar{id}, 'dom', 'group');
272 my $dom = domainName($dbh, $webvar{id});
273 my ($code,$msg) = delDomain($dbh, $webvar{id});
274 if ($code ne 'OK') {
275# need to find failure mode
276 logaction($webvar{id}, $session->param("username"), $pargroup, "Failed to delete domain $dom ($msg)");
277 changepage(page => "domlist", errmsg => "Error deleting domain $dom: $msg");
278 } else {
279 logaction($webvar{id}, $session->param("username"), $pargroup, "Deleted domain $dom");
280 changepage(page => "domlist");
281 }
282
283 } else {
284 # cancelled. whee!
285 changepage(page => "domlist");
286 }
287
288} elsif ($webvar{page} eq 'reclist') {
289
290 $page->param(mayeditsoa => $permissions{admin} || $permissions{domain_edit});
291##fixme: ACL needs pondering. Does "edit domain" interact with record add/remove/etc?
292# Note this seems to be answered "no" in Vega.
293# ACLs
294 $page->param(record_create => ($permissions{admin} || $permissions{record_create}) );
295# $page->param(record_edit => ($permissions{admin} || $permissions{record_edit}) );
296 $page->param(record_delete => ($permissions{admin} || $permissions{record_delete}) );
297
298 # Handle record list for both default records (per-group) and live domain records
299
300 $page->param(defrec => $webvar{defrec});
301 $page->param(id => $webvar{id});
302 $page->param(curpage => $webvar{page});
303
304 my $count = getRecCount($dbh, $webvar{defrec}, $webvar{id});
305
306# $sortby = 'host';
307# sort/order
308 $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
309 $session->param($webvar{page}.'order', $webvar{order}) if $webvar{order};
310
311 $sortby = $session->param($webvar{page}.'sortby');
312 $sortorder = $session->param($webvar{page}.'order');
313
314# set up the headers
315 my @cols = ('host', 'type', 'val', 'distance', 'weight', 'port', 'ttl');
316 my %colheads = (host => 'Name', type => 'Type', val => 'Address',
317 distance => 'Distance', weight => 'Weight', port => 'Port', ttl => 'TTL');
318 my %custom = (id => $webvar{id}, defrec => $webvar{defrec});
319 fill_colheads($sortby, $sortorder, \@cols, \%colheads, \%custom);
320
321# fill the page-count and first-previous-next-last-all details
322 fill_pgcount($count,"records",
323 ($webvar{defrec} eq 'y' ? "group ".groupName($dbh,$webvar{id}) : domainName($dbh,$webvar{id})));
324 fill_fpnla($count); # should put some params on this sub...
325
326 $page->param(defrec => $webvar{defrec});
327 if ($webvar{defrec} eq 'y') {
328 showdomain('y',$curgroup);
329 } else {
330 showdomain('n',$webvar{id});
331 $page->param(logdom => 1);
332 }
333
334 $page->param(errmsg => $webvar{errmsg}) if $webvar{errmsg};
335
336} elsif ($webvar{page} eq 'record') {
337
338 if ($webvar{recact} eq 'new') {
339
340 changepage(page => "reclist", errmsg => "You are not permitted to add records", id => $webvar{parentid})
341 unless ($permissions{admin} || $permissions{record_create});
342
343 $page->param(todo => "Add record");
344 $page->param(recact => "add");
345 $page->param(parentid => $webvar{parentid});
346 $page->param(defrec => $webvar{defrec});
347
348 fill_recdata();
349
350 } elsif ($webvar{recact} eq 'add') {
351
352 changepage(page => "reclist", errmsg => "You are not permitted to add records", id => $webvar{parentid})
353 unless ($permissions{admin} || $permissions{record_create});
354
355 my @recargs = ($dbh,$webvar{defrec},$webvar{parentid},$webvar{name},$webvar{type},$webvar{address},$webvar{ttl});
356 if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV}) {
357 push @recargs, $webvar{distance};
358 if ($webvar{type} == $reverse_typemap{SRV}) {
359 push @recargs, $webvar{weight};
360 push @recargs, $webvar{port};
361 }
362 }
363
364 my ($code,$msg) = addRec(@recargs);
365
366 if ($code eq 'OK') {
367 if ($webvar{defrec} eq 'y') {
368 logaction(0, $session->param("username"), $webvar{parentid},
369 "Added default record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl}");
370 } else {
371 logaction($webvar{parentid}, $session->param("username"), parentID($webvar{parentid}, 'dom', 'group'),
372 "Added record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl}");
373 }
374 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec});
375 } else {
376 $page->param(failed => 1);
377 $page->param(errmsg => $msg);
378 $page->param(wastrying => "adding");
379 $page->param(todo => "Add record");
380 $page->param(recact => "add");
381 $page->param(parentid => $webvar{parentid});
382 $page->param(defrec => $webvar{defrec});
383 $page->param(id => $webvar{id});
384 fill_recdata(); # populate the form... er, mostly.
385 if ($webvar{defrec} eq 'y') {
386 logaction(0, $session->param("username"), $webvar{parentid},
387 "Failed adding default record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl} ($msg)");
388 } else {
389 logaction($webvar{parentid}, $session->param("username"), parentID($webvar{parentid}, 'dom', 'group'),
390 "Failed adding record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl} ($msg)");
391 }
392 }
393
394 } elsif ($webvar{recact} eq 'edit') {
395
396 changepage(page => "reclist", errmsg => "You are not permitted to edit records", id => $webvar{parentid})
397 unless ($permissions{admin} || $permissions{record_edit});
398
399 $page->param(todo => "Update record");
400 $page->param(recact => "update");
401 $page->param(parentid => $webvar{parentid});
402 $page->param(id => $webvar{id});
403 $page->param(defrec => $webvar{defrec});
404 my $recdata = getRecLine($dbh, $webvar{defrec}, $webvar{id});
405 $page->param(name => $recdata->{host});
406 $page->param(address => $recdata->{val});
407 $page->param(distance => $recdata->{distance});
408 $page->param(weight => $recdata->{weight});
409 $page->param(port => $recdata->{port});
410 $page->param(ttl => $recdata->{ttl});
411 fill_rectypes($recdata->{type});
412
413 } elsif ($webvar{recact} eq 'update') {
414
415 changepage(page => "reclist", errmsg => "You are not permitted to edit records", id => $webvar{parentid})
416 unless ($permissions{admin} || $permissions{record_edit});
417
418 my ($code,$msg) = updateRec($dbh,$webvar{defrec},$webvar{id},
419 $webvar{name},$webvar{type},$webvar{address},$webvar{ttl},
420 $webvar{distance},$webvar{weight},$webvar{port});
421
422 if ($code eq 'OK') {
423##fixme: need more magic to get proper group - if domain was fiddled
424# from search-subgroups listing, may not be "current" group
425
426# SELECT d.group_id FROM domains d INNER JOIN records r ON d.domain_id=r.domain_id WHERE r.record_id=?
427# $sth->execute($webvar{id});
428##log
429 if ($webvar{defrec} eq 'y') {
430 logaction(0, $session->param("username"), $webvar{parentid},
431 "Updated default record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl}");
432 } else {
433 logaction($webvar{parentid}, $session->param("username"), parentID($webvar{id}, 'rec', 'group'),
434 "Updated record '$webvar{name} $typemap{$webvar{type}} $webvar{address}', TTL $webvar{ttl}");
435 }
436 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec});
437 } else {
438 $page->param(failed => 1);
439 $page->param(errmsg => $msg);
440 $page->param(wastrying => "updating");
441 $page->param(todo => "Update record");
442 $page->param(recact => "update");
443 $page->param(parentid => $webvar{parentid});
444 $page->param(defrec => $webvar{defrec});
445 $page->param(id => $webvar{id});
446 fill_recdata();
447 if ($webvar{defrec} eq 'y') {
448 logaction(0, $session->param("username"), $webvar{parentid},
449 "Failed updating default record '$typemap{$webvar{type}} $webvar{name} $webvar{address}', TTL $webvar{ttl} ($msg)");
450 } else {
451 logaction($webvar{parentid}, $session->param("username"), parentID($webvar{parentid}, 'dom', 'group'),
452 "Failed updating record '$typemap{$webvar{type}} $webvar{name} $webvar{address}', TTL $webvar{ttl} ($msg)");
453 }
454 }
455 }
456
457 if ($webvar{defrec} eq 'y') {
458 $page->param(dohere => "default records in group ".groupName($dbh,$webvar{parentid}));
459 } else {
460 $page->param(parentid => $webvar{parentid});
461 $page->param(dohere => domainName($dbh,$webvar{parentid}));
462 }
463
464} elsif ($webvar{page} eq 'delrec') {
465
466 # This is a complete separate segment since it uses a different template from add/edit records above
467
468 changepage(page => "reclist", errmsg => "You are not permitted to delete records", id => $webvar{parentid})
469 unless ($permissions{admin} || $permissions{record_delete});
470
471 $page->param(id => $webvar{id});
472 $page->param(defrec => $webvar{defrec});
473 $page->param(parentid => $webvar{parentid});
474 # first pass = confirm y/n (sorta)
475 if (!defined($webvar{del})) {
476 $page->param(del_getconf => 1);
477 my $rec = getRecLine($dbh,$webvar{defrec},$webvar{id});
478 $page->param(host => $rec->{host});
479 $page->param(ftype => $typemap{$rec->{type}});
480 $page->param(recval => $rec->{val});
481 } elsif ($webvar{del} eq 'ok') {
482# get rec data before we try to delete it
483 my $rec = getRecLine($dbh,$webvar{defrec},$webvar{id});
484 my ($code,$msg) = delRec($dbh,$webvar{defrec},$webvar{id});
485 if ($code ne 'OK') {
486## need to find failure mode
487 if ($webvar{defrec} eq 'y') {
488 logaction(0, $session->param("username"), $rec->{parid},
489 "Failed deleting default record '$rec->{host} $typemap{$rec->{type}} $rec->{val}',".
490 " TTL $rec->{ttl} ($msg)");
491 } else {
492 logaction($rec->{parid}, $session->param("username"), parentID($rec->{parid}, 'dom', 'group'),
493 "Failed deleting record '$rec->{host} $typemap{$rec->{type}} $rec->{val}', TTL $rec->{ttl} ($msg)");
494 }
495 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec},
496 errmsg => "Error deleting record: $msg");
497# $page->param(del_failed => 1);
498# $page->param(errmsg => $msg);
499# showdomain($webvar{defrec}, $webvar{parentid});
500 } else {
501 if ($webvar{defrec} eq 'y') {
502 logaction(0, $session->param("username"), $rec->{parid},
503 "Deleted default record '$rec->{host} $typemap{$rec->{type}} $rec->{val}', TTL $rec->{ttl}");
504 } else {
505 logaction($rec->{parid}, $session->param("username"), parentID($rec->{parid}, 'dom', 'group'),
506 "Deleted record '$rec->{host} $typemap{$rec->{type}} $rec->{val}', TTL $rec->{ttl}");
507 }
508 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec});
509 }
510 } else {
511 changepage(page => "reclist", id => $webvar{parentid}, defrec => $webvar{defrec});
512 }
513
514} elsif ($webvar{page} eq 'editsoa') {
515
516 changepage(page => "reclist", errmsg => "You are not permitted to edit domain SOA records", id => $webvar{id})
517 unless ($permissions{admin} || $permissions{domain_edit});
518
519 fillsoa($webvar{defrec},$webvar{id});
520
521} elsif ($webvar{page} eq 'updatesoa') {
522
523 changepage(page => "reclist", errmsg => "You are not permitted to edit domain SOA records", id => $webvar{id})
524 unless ($permissions{admin} || $permissions{domain_edit});
525
526 my $sth;
527 my $sql = '';
528 # no domain ID, so we're editing the default SOA for a group (we don't care which one here)
529 # plus a bit of magic to update the appropriate table
530 $sql = "update ".($webvar{defrec} eq 'y' ? "default_records" : "records").
531 " set host='$webvar{prins}:$webvar{contact}',".
532 " val='$webvar{refresh}:$webvar{retry}:$webvar{expire}:$webvar{minttl}',".
533 " ttl=$webvar{ttl} where record_id=$webvar{recid}";
534 $sth = $dbh->prepare($sql);
535 $sth->execute;
536
537 if ($sth->err) {
538 $page->param(update_failed => 1);
539 $page->param(msg => $DBI::errstr);
540 fillsoa($webvar{defrec},$webvar{id});
541 } else {
542
543##fixme! need to set group ID properly here
544# SELECT group_id FROM domains WHERE domain_id=?
545# $sth->execute($webvar{id});
546##log
547 logaction(0, $session->param("username"), $webvar{group},
548 "Updated SOA (ns $webvar{prins}, contact $webvar{contact}, refresh $webvar{refresh},".
549 " retry $webvar{retry}, expire $webvar{expire}, minTTL $webvar{minttl}, TTL $webvar{ttl}");
550 changepage(page => "reclist", id => $webvar{id}, defrec => $webvar{defrec});
551# $page->param(update_failed => 0);
552# showdomain('y',1);
553 }
554
555} elsif ($webvar{page} eq 'grpman') {
556
557 listgroups();
558 $page->param(curpage => $webvar{page});
559
560} elsif ($webvar{page} eq 'newgrp') {
561
562 changepage(page => "grpman", errmsg => "You are not permitted to add groups", id => $webvar{parentid})
563 unless ($permissions{admin} || $permissions{group_add});
564
565 # do.. uhh.. stuff.. if we have no webvar{action}
566 if ($webvar{action} && $webvar{action} eq 'add') {
567 my %newperms;
568 foreach (@permtypes) {
569 $newperms{$_} = 0;
570 $newperms{$_} = (defined($webvar{$_}) && $webvar{$_} eq 'on' ? 1 : 0);
571 }
572 # not gonna provide the 4th param: template-or-clone flag, just yet
573 my ($code,$msg) = addGroup($dbh, $webvar{newgroup}, $webvar{pargroup}, \%newperms);
574 if ($code eq 'OK') {
575 logaction(0, $session->param("username"), $webvar{pargroup}, "Added group $webvar{newgroup}");
576 changepage(page => "grpman");
577 }
578 # no point in doing extra work
579 fill_permissions($page, \%newperms);
580 $page->param(add_failed => 1);
581 $page->param(errmsg => $msg);
582 $page->param(newgroup => $webvar{newgroup});
583 fill_grouplist('pargroup',$webvar{pargroup});
584 } else {
585 fill_grouplist('pargroup',$curgroup);
586 # fill default permissions with immediate parent's current ones
587 my %parperms;
588 getPermissions($dbh, 'group', $curgroup, \%parperms);
589 fill_permissions($page, \%parperms);
590 }
591
592} elsif ($webvar{page} eq 'delgrp') {
593
594 changepage(page => "grpman", errmsg => "You are not permitted to delete groups", id => $webvar{parentid})
595 unless ($permissions{admin} || $permissions{group_delete});
596
597 $page->param(id => $webvar{id});
598 # first pass = confirm y/n (sorta)
599 if (!defined($webvar{del})) {
600 $page->param(del_getconf => 1);
601# $page->param(groupname => groupName($dbh,$webvar{id}));
602# print some neato things?
603
604# } else {
605# #whether actually deleting or cancelling we redirect to the group list, default format
606
607 } elsif ($webvar{del} eq 'ok') {
608 my $deleteme = groupName($dbh,$webvar{id}); # get this before we delete it...
609 my ($code,$msg) = delGroup($dbh, $webvar{id});
610 if ($code ne 'OK') {
611# need to find failure mode
612 logaction(0, $session->param("username"), $webvar{curgroup}, "Failure deleting group $deleteme: $msg");
613 changepage(page => "grpman", del_failed => 1, errmsg => $msg);
614 } else {
615##fixme: need to clean up log when deleting a major container
616 logaction(0, $session->param("username"), $webvar{curgroup}, "Deleted group $deleteme");
617 # success. go back to the domain list, do not pass "GO"
618 changepage(page => "grpman");
619 }
620 } else {
621 # cancelled. whee!
622 changepage(page => "grpman");
623 }
624 $page->param(delgroupname => groupName($dbh, $webvar{id}));
625
626} elsif ($webvar{page} eq 'edgroup') {
627
628 changepage(page => "grpman", errmsg => "You are not permitted to edit groups", id => $webvar{parentid})
629 unless ($permissions{admin} || $permissions{group_edit});
630
631 if ($webvar{action} eq 'updperms') {
632 # extra safety check; make sure user can't construct a URL to bypass ACLs
633 my %curperms;
634 getPermissions($dbh, 'group', $webvar{gid}, \%curperms);
635 my %chperms;
636 foreach (@permtypes) {
637 $webvar{$_} = 0 if !defined($webvar{$_});
638 $webvar{$_} = 1 if $webvar{$_} eq 'on';
639 $chperms{$_} = $webvar{$_} if $curperms{$_} ne $webvar{$_};
640 }
641 my ($code,$msg) = changePermissions($dbh, 'group', $webvar{gid}, \%chperms);
642 if ($code eq 'OK') {
643 logaction(0, $session->param("username"), $webvar{gid}, "Changed default permissions in group $webvar{gid}");
644 changepage(page => "grpman");
645 }
646 # no point in doing extra work
647 fill_permissions($page, \%chperms);
648 $page->param(errmsg => $msg);
649 }
650 $page->param(gid => $webvar{gid});
651 $page->param(grpmeddle => groupName($dbh, $webvar{gid}));
652 my %grpperms;
653 getPermissions($dbh, 'group', $webvar{gid}, \%grpperms);
654 fill_permissions($page, \%grpperms);
655
656} elsif ($webvar{page} eq 'bulkdomain') {
657 # Bulk operations on domains. Note all but group move are available on the domain list.
658
659 changepage(page => "domlist", errmsg => "You are not permitted to make bulk domain changes")
660 unless ($permissions{admin} || $permissions{domain_edit} || $permissions{domain_create} || $permissions{domain_delete});
661
662##fixme
663##fixme push the SQL and direct database fiddling off into a sub in DNSDB.pm
664##fixme
665
666##fixme: un-hardcode the limit?
667# $perpage = 50;
668
669 my $sth = $dbh->prepare("SELECT count(*) FROM domains WHERE group_id=?");
670 $sth->execute($curgroup);
671 my ($count) = ($sth->fetchrow_array);
672
673 $page->param(curpage => $webvar{page});
674 fill_pgcount($count,'domains',groupName($dbh,$curgroup));
675 fill_fpnla($count);
676 $page->param(offset => $offset); # since apparently this isn't set explicitly elsewhere. Who knew?
677 $page->param(perpage => $perpage);
678
679 my @domlist;
680 my $sql = "SELECT domain_id,domain FROM domains".
681 " WHERE group_id=?".
682 " ORDER BY domain".
683 ($offset eq 'all' ? '' : " LIMIT $perpage OFFSET ".$offset*$perpage);
684 $sth = $dbh->prepare($sql);
685 $sth->execute($curgroup);
686 my $rownum = 0;
687 while (my @data = $sth->fetchrow_array) {
688 my %row;
689 $row{domid} = $data[0];
690 $row{domain} = $data[1];
691 $rownum++; # putting this in the expression below causes failures. *eyeroll*
692 $row{newrow} = $rownum % 5 == 0;
693 push @domlist, \%row;
694 }
695 $page->param(domtable => \@domlist);
696 # ACLs
697 $page->param(maymove => ($permissions{admin} || ($permissions{domain_edit} && $permissions{domain_create} && $permissions{domain_delete})));
698 $page->param(maystatus => $permissions{admin} || $permissions{domain_edit});
699 $page->param(maydelete => $permissions{admin} || $permissions{domain_delete});
700
701} elsif ($webvar{page} eq 'bulkchange') {
702
703 if ($webvar{action} eq 'move') {
704 changepage(page => "domlist", errmsg => "You are not permitted to bulk-move domains")
705 unless ($permissions{admin} || ($permissions{domain_edit} && $permissions{domain_create} && $permissions{domain_delete}));
706 my $newgname = groupName($dbh,$webvar{destgroup});
707 $page->param(action => "Move to group $newgname");
708 my @bulkresults;
709 # nngh. due to alpha-sorting on the previous page, we can't use domid-numeric
710 # order here, and since we don't have the domain names until we go around this
711 # loop, we can't alpha-sort them here. :(
712 foreach (keys %webvar) {
713 my %row;
714 next unless $_ =~ /^dom_\d+$/;
715 $row{domain} = domainName($dbh,$webvar{$_});
716 my ($code, $msg) = changeGroup($dbh, 'domain', $webvar{$_}, $webvar{destgroup});
717 if ($code eq 'OK') {
718 logaction($webvar{$_}, $session->param("username"), parentID($webvar{$_}, 'dom', 'group'),
719 "Moved domain ".domainName($dbh, $webvar{$_})." to group $newgname");
720 $row{domok} = ($code eq 'OK');
721 } else {
722 logaction($webvar{$_}, $session->param("username"), parentID($webvar{$_}, 'dom', 'group'),
723 "Failure moving domain ".domainName($dbh, $webvar{$_})." to group $newgname: $msg");
724 }
725 $row{domerr} = $msg;
726 push @bulkresults, \%row;
727 }
728 $page->param(bulkresults => \@bulkresults);
729
730 } elsif ($webvar{action} eq 'deactivate' || $webvar{action} eq 'activate') {
731 changepage(page => "domlist", errmsg => "You are not permitted to bulk-$webvar{action} domains")
732 unless ($permissions{admin} || $permissions{domain_edit});
733 $page->param(action => "$webvar{action} domains");
734 my @bulkresults;
735 foreach (keys %webvar) {
736 my %row;
737 next unless $_ =~ /^dom_\d+$/;
738 $row{domain} = domainName($dbh,$webvar{$_});
739##fixme: error handling on status change
740 my $stat = domStatus($dbh,$webvar{$_},($webvar{action} eq 'activate' ? 'domon' : 'domoff'));
741 logaction($webvar{$_}, $session->param("username"), parentID($webvar{$_}, 'dom', 'group'),
742 "Changed domain ".domainName($dbh, $webvar{$_})." state to ".($stat ? 'active' : 'inactive'));
743 $row{domok} = 1;
744# $row{domok} = ($code eq 'OK');
745# $row{domerr} = $msg;
746 push @bulkresults, \%row;
747 }
748 $page->param(bulkresults => \@bulkresults);
749
750 } elsif ($webvar{action} eq 'delete') {
751 changepage(page => "domlist", errmsg => "You are not permitted to bulk-delete domains")
752 unless ($permissions{admin} || $permissions{domain_delete});
753 $page->param(action => "$webvar{action} domains");
754 my @bulkresults;
755 foreach (keys %webvar) {
756 my %row;
757 next unless $_ =~ /^dom_\d+$/;
758 $row{domain} = domainName($dbh,$webvar{$_});
759 my $pargroup = parentID($webvar{$_}, 'dom', 'group');
760 my $dom = domainName($dbh, $webvar{$_});
761 my ($code, $msg) = delDomain($dbh, $webvar{$_});
762 if ($code eq 'OK') {
763 logaction($webvar{$_}, $session->param("username"), $pargroup, "Deleted domain $dom");
764 $row{domok} = ($code eq 'OK');
765 } else {
766 logaction($webvar{$_}, $session->param("username"), $pargroup, "Failure deleting domain $dom: $msg");
767 }
768 $row{domerr} = $msg;
769 push @bulkresults, \%row;
770 }
771 $page->param(bulkresults => \@bulkresults);
772
773 } # move/(de)activate/delete if()
774
775 # not going to handle the unknown $webvar{action} else; it should not be possible in normal
776 # operations, and anyone who meddles with the URL gets what they deserve.
777
778} elsif ($webvar{page} eq 'useradmin') {
779
780 if (defined($webvar{action})) {
781 userStatus($dbh,$webvar{id},$webvar{action});
782 }
783
784 $page->param(curpage => $webvar{page});
785
786 list_users();
787
788} elsif ($webvar{page} eq 'user') {
789
790 # All user add/edit actions fall through the same page, since there aren't
791 # really any hard differences between the templates
792
793 #fill_actypelist($webvar{accttype});
794 fill_clonemelist();
795 my %grpperms;
796 getPermissions($dbh, 'group', $curgroup, \%grpperms);
797
798 my $grppermlist = new HTML::Template(filename => "$templatedir/permlist.tmpl");
799 my %noaccess;
800 fill_permissions($grppermlist, \%grpperms, \%noaccess);
801 $grppermlist->param(info => 1);
802 $page->param(grpperms => $grppermlist->output);
803
804 $page->param(is_admin => $permissions{admin});
805
806 $webvar{action} = '' if !$webvar{action};
807
808 if ($webvar{action} eq 'add' or $webvar{action} eq 'update') {
809
810 $page->param(add => 1) if $webvar{action} eq 'add';
811
812 my ($code,$msg);
813
814 my $alterperms = 0; # flag iff we need to force custom permissions due to user's current access limits
815
816 my %newperms; # we're going to prefill the existing permissions, so we can change them.
817 getPermissions($dbh, 'user', $webvar{uid}, \%newperms);
818
819 if ($webvar{pass1} ne $webvar{pass2}) {
820 $code = 'FAIL';
821 $msg = "Passwords don't match";
822 } else {
823
824 # assemble a permission string - far simpler than trying to pass an
825 # indeterminate set of permission flags individually
826
827 # But first, we have to see if the user can add any particular
828 # permissions; otherwise we have a priviledge escalation. Whee.
829
830 if (!$permissions{admin}) {
831 my %grpperms;
832 getPermissions($dbh, 'group', $curgroup, \%grpperms);
833 my $ret = comparePermissions(\%permissions, \%grpperms);
834 if ($ret ne '<' && $ret ne '!') {
835 # User's permissions are not a superset or equivalent to group. Can't inherit
836 # (and include access user doesn't currently have), so we force custom.
837 $webvar{perms_type} = 'custom';
838 $alterperms = 1;
839 }
840 }
841
842 my $permstring;
843 if ($webvar{perms_type} eq 'custom') {
844 $permstring = 'C:';
845 foreach (@permtypes) {
846 if ($permissions{admin} || $permissions{$_}) {
847 $permstring .= ",$_" if defined($webvar{$_}) && $webvar{$_} eq 'on';
848 $newperms{$_} = (defined($webvar{$_}) && $webvar{$_} eq 'on' ? 1 : 0);
849 }
850 }
851 $page->param(perm_custom => 1);
852 } elsif ($permissions{admin} && $webvar{perms_type} eq 'clone') {
853 $permstring = "c:$webvar{clonesrc}";
854 getPermissions($dbh, 'user', $webvar{clonesrc}, \%newperms);
855 $page->param(perm_clone => 1);
856 } else {
857 $permstring = 'i';
858 }
859 if ($webvar{action} eq 'add') {
860 ($code,$msg) = addUser($dbh, $webvar{uname}, $curgroup, $webvar{pass1},
861 ($webvar{makeactive} eq 'on' ? 1 : 0), $webvar{accttype}, $permstring,
862 $webvar{fname}, $webvar{lname}, $webvar{phone});
863 logaction(0, $session->param("username"), $curgroup, "Added user $webvar{uname} (uid $msg)")
864 if $code eq 'OK';
865 } else {
866# User update is icky. I'd really like to do this in one atomic
867# operation, but that would duplicate a **lot** of code in DNSDB.pm
868 # Allowing for changing group, but not coding web support just yet.
869 ($code,$msg) = updateUser($dbh, $webvar{uid}, $webvar{uname}, $webvar{gid}, $webvar{pass1},
870 ($webvar{makeactive} eq 'on' ? 1 : 0), $webvar{accttype},
871 $webvar{fname}, $webvar{lname}, $webvar{phone});
872 if ($code eq 'OK') {
873 $newperms{admin} = 1 if $webvar{accttype} eq 'S';
874 ($code,$msg) = changePermissions($dbh, 'user', $webvar{uid}, \%newperms, ($permstring eq 'i'));
875 }
876 logaction(0, $session->param("username"), $curgroup,
877 "Updated uid $webvar{uid}, user $webvar{uname} ($webvar{fname} $webvar{lname})");
878 }
879 }
880
881 if ($code eq 'OK') {
882
883 if ($alterperms) {
884 changepage(page => "useradmin", warnmsg =>
885 "You can only grant permissions you hold. $webvar{uname} ".
886 ($webvar{action} eq 'add' ? 'added' : 'updated')." with reduced access.");
887 } else {
888 changepage(page => "useradmin");
889 }
890
891 # add/update failed:
892 } else {
893 $page->param(add_failed => 1);
894 $page->param(action => $webvar{action});
895 $page->param(set_permgroup => 1);
896 if ($webvar{perms_type} eq 'inherit') { # set permission class radio
897 $page->param(perm_inherit => 1);
898 } elsif ($webvar{perms_type} eq 'clone') {
899 $page->param(perm_clone => 1);
900 } else {
901 $page->param(perm_custom => 1);
902 }
903 $page->param(uname => $webvar{uname});
904 $page->param(fname => $webvar{fname});
905 $page->param(lname => $webvar{lname});
906 $page->param(pass1 => $webvar{pass1});
907 $page->param(pass2 => $webvar{pass2});
908 $page->param(errmsg => $msg);
909 fill_permissions($page, \%newperms);
910 fill_actypelist($webvar{accttype});
911 fill_clonemelist();
912##fixme: log
913 }
914
915 } elsif ($webvar{action} eq 'edit') {
916
917 $page->param(set_permgroup => 1);
918 $page->param(action => 'update');
919 $page->param(uid => $webvar{user});
920 fill_clonemelist();
921
922 my $userinfo = getUserData($dbh,$webvar{user});
923 fill_actypelist($userinfo->{type});
924 # not using this yet, but adding it now means we can *much* more easily do so later.
925 $page->param(gid => $webvar{group_id});
926
927 my %curperms;
928 getPermissions($dbh, 'user', $webvar{user}, \%curperms);
929 fill_permissions($page, \%curperms);
930
931 $page->param(uname => $userinfo->{username});
932 $page->param(fname => $userinfo->{firstname});
933 $page->param(lname => $userinfo->{lastname});
934 $page->param(set_permgroup => 1);
935 if ($userinfo->{inherit_perm}) {
936 $page->param(perm_inherit => 1);
937 } else {
938 $page->param(perm_custom => 1);
939 }
940##work
941# } elsif ($webvar{action} eq 'update') {
942 } else {
943 # default is "new"
944 $page->param(add => 1);
945 $page->param(action => 'add');
946 fill_permissions($page, \%grpperms);
947 fill_actypelist();
948 }
949
950} elsif ($webvar{page} eq 'newuser') {
951
952 # foo?
953 fill_actypelist();
954 fill_clonemelist();
955
956 my %grpperms;
957 getPermissions($dbh, 'group', $curgroup, \%grpperms);
958 fill_permissions($page, \%grpperms);
959
960 my $grppermlist = new HTML::Template(filename => "$templatedir/permlist.tmpl");
961 my %noaccess;
962 fill_permissions($grppermlist, \%grpperms, \%noaccess);
963 $grppermlist->param(info => 1);
964 $page->param(grpperms => $grppermlist->output);
965
966#} elsif ($webvar{page} eq 'adduser') {
967#
968# my ($code,$msg);
969#
970# if ($webvar{pass1} ne $webvar{pass2}) {
971# $code = 'FAIL';
972# $msg = "Passwords don't match";
973# } else {
974## assemble a permission string - far simpler than trying to pass an
975## indeterminate set of permission flags individually
976#my $permstring;
977#if ($webvar{perms_type} eq 'custom') {
978# $permstring = 'C:,g:,u:,d:,r:';
979# $page->param(perm_custom => 1);
980#} elsif ($webvar{perms_type} eq 'clone') {
981# $permstring = 'c:';
982# $page->param(perm_clone => 1);
983#} else {
984# $permstring = 'i';
985## $page->param(perm_inherit => 1);
986#}
987# ($code,$msg) = addUser($dbh,$webvar{uname}, $webvar{group}, $webvar{pass1},
988# ($webvar{makeactive} eq 'on' ? 1 : 0), $webvar{accttype},
989# $webvar{fname}, $webvar{lname}, $webvar{phone});
990# }
991#
992## hokay, a bit of magic to decide which page we hit.
993# if ($code eq 'OK') {
994###log
995# logaction(0, $session->param("username"), $webvar{group},
996# "Added user $webvar{uname} ($webvar{fname} $webvar{lname})");
997# changepage(page => "useradmin");
998# } else {
999## oddity - apparently, xhtml 1.0 strict swallows username as an HTML::Template var. O_o
1000# $page->param(add_failed => 1);
1001# $page->param(uname => $webvar{uname});
1002# $page->param(fname => $webvar{fname});
1003# $page->param(lname => $webvar{lname});
1004# $page->param(pass1 => $webvar{pass1});
1005# $page->param(pass2 => $webvar{pass2});
1006# $page->param(errmsg => $msg);
1007# fill_actypelist($webvar{accttype});
1008# fill_clonemelist();
1009# }
1010#
1011## $page->param(add_failed => 1);
1012#
1013
1014} elsif ($webvar{page} eq 'deluser') {
1015
1016 $page->param(id => $webvar{id});
1017 # first pass = confirm y/n (sorta)
1018 if (!defined($webvar{del})) {
1019 $page->param(del_getconf => 1);
1020 $page->param(user => userFullName($dbh,$webvar{id}));
1021 } elsif ($webvar{del} eq 'ok') {
1022##fixme: find group id user is in (for logging) *before* we delete the user
1023##fixme: get other user data too for log
1024 my $userref = getUserData($dbh, $webvar{id});
1025 my ($code,$msg) = delUser($dbh, $webvar{id});
1026 if ($code ne 'OK') {
1027# need to find failure mode
1028 $page->param(del_failed => 1);
1029 $page->param(errmsg => $msg);
1030 list_users($curgroup);
1031 } else {
1032 # success. go back to the user list, do not pass "GO"
1033 # actions on users have a domain id of 0, always
1034 logaction(0, $session->param("username"), $curgroup, "Deleted user $webvar{id}/".$userref->{username}.
1035 " (".$userref->{lastname}.", ".$userref->{firstname}.")");
1036 changepage(page => "useradmin");
1037 }
1038 } else {
1039 # cancelled. whee!
1040 changepage(page => "useradmin");
1041 }
1042
1043#} elsif ($webvar{page} eq 'edituser') {
1044
1045} elsif ($webvar{page} eq 'dnsq') {
1046
1047 $page->param(qfor => $webvar{qfor}) if $webvar{qfor};
1048 fill_rectypes($webvar{type} ? $webvar{type} : '', 1);
1049 $page->param(nrecurse => $webvar{nrecurse}) if $webvar{nrecurse};
1050 $page->param(resolver => $webvar{resolver}) if $webvar{resolver};
1051
1052 if ($webvar{qfor}) {
1053 my $resolv = Net::DNS::Resolver->new;
1054 $resolv->tcp_timeout(5); # make me adjustable!
1055 $resolv->udp_timeout(5); # make me adjustable!
1056 $resolv->recurse(0) if $webvar{nrecurse};
1057 $resolv->nameservers($webvar{resolver}) if $webvar{resolver};
1058 my $query = $resolv->query($webvar{qfor}, $typemap{$webvar{type}});
1059 if ($query) {
1060
1061 $page->param(showresults => 1);
1062
1063 my @answer;
1064 foreach my $rr ($query->answer) {
1065# next unless $rr->type eq "A" or $rr->type eq 'NS';
1066 my %row;
1067 my ($host,$ttl,$class,$type,$data) =
1068 ($rr->string =~ /^([0-9a-zA-Z_.-]+)\s+(\d+)\s+([A-Za-z]+)\s+([A-Za-z]+)\s+(.+)$/s);
1069 $row{host} = $host;
1070 $row{ftype} = $type;
1071 $row{rdata} = ($type eq 'SOA' ? "<pre>$data</pre>" : $data);
1072 push @answer, \%row;
1073 }
1074 $page->param(answer => \@answer);
1075
1076 my @additional;
1077 foreach my $rr ($query->additional) {
1078# next unless $rr->type eq "A" or $rr->type eq 'NS';
1079 my %row;
1080 my ($host,$ttl,$class,$type,$data) =
1081 ($rr->string =~ /^([0-9a-zA-Z_.-]+)\s+(\d+)\s+([A-Za-z]+)\s+([A-Za-z]+)\s+(.+)$/);
1082 $row{host} = $host;
1083 $row{ftype} = $type;
1084 $row{rdata} = $data;
1085 push @additional, \%row;
1086 }
1087 $page->param(additional => \@additional);
1088
1089 my @authority;
1090 foreach my $rr ($query->authority) {
1091# next unless $rr->type eq "A" or $rr->type eq 'NS';
1092 my %row;
1093 my ($host,$ttl,$class,$type,$data) =
1094 ($rr->string =~ /^([0-9a-zA-Z_.-]+)\s+(\d+)\s+([A-Za-z]+)\s+([A-Za-z]+)\s+(.+)$/);
1095 $row{host} = $host;
1096 $row{ftype} = $type;
1097 $row{rdata} = $data;
1098 push @authority, \%row;
1099 }
1100 $page->param(authority => \@authority);
1101
1102 $page->param(usedresolver => $resolv->answerfrom);
1103 $page->param(frtype => $typemap{$webvar{type}});
1104
1105 } else {
1106 $page->param(errmsg => $resolv->errorstring);
1107 }
1108 }
1109 ## done DNS query
1110
1111} elsif ($webvar{page} eq 'axfr') {
1112
1113 changepage(page => "domlist", errmsg => "You are not permitted to import domains")
1114 unless ($permissions{admin} || $permissions{domain_create});
1115
1116 # don't need this while we've got the dropdown in the menu. hmm.
1117 #fill_grouplist;
1118
1119 $page->param(ifrom => $webvar{ifrom}) if $webvar{ifrom};
1120 $page->param(rwsoa => $webvar{rwsoa}) if $webvar{rwsoa};
1121 $page->param(rwns => $webvar{rwns}) if $webvar{rwns};
1122 $page->param(dominactive => 1) if (!$webvar{domactive} && $webvar{doit}); # eww.
1123 $page->param(importdoms => $webvar{importdoms}) if $webvar{importdoms};
1124
1125 # shut up warning about uninitialized variable
1126 $webvar{doit} = '' if !defined($webvar{doit});
1127
1128 if ($webvar{doit} eq 'y' && !$webvar{ifrom}) {
1129 $page->param(errmsg => "Need to set host to import from");
1130 } elsif ($webvar{doit} eq 'y' && !$webvar{importdoms}) {
1131 $page->param(errmsg => "Need domains to import");
1132 } elsif ($webvar{doit} eq 'y') {
1133 my @domlist = split /\s+/, $webvar{importdoms};
1134 my @results;
1135 foreach my $domain (@domlist) {
1136 my %row;
1137 my ($code,$msg) = importAXFR($dbh, $webvar{ifrom}, $domain, $webvar{group},
1138 $webvar{domstatus}, $webvar{rwsoa}, $webvar{rwns});
1139 $row{domok} = $msg if $code eq 'OK';
1140 if ($code eq 'WARN') {
1141 $msg =~ s|\n|<br />|g;
1142 $row{domwarn} = $msg;
1143 }
1144 if ($code eq 'FAIL') {
1145 $msg =~ s|\n|<br />\n|g;
1146 $row{domerr} = $msg;
1147 }
1148 $msg = "<br />\n".$msg if $msg =~ m|<br />|;
1149 logaction(domainID($dbh, $domain), $session->param("username"), $webvar{group},
1150 "AXFR import $domain from $webvar{ifrom} ($code): $msg");
1151 $row{domain} = $domain;
1152 push @results, \%row;
1153 }
1154 $page->param(axfrresults => \@results);
1155 }
1156
1157} elsif ($webvar{page} eq 'whoisq') {
1158
1159 if ($webvar{qfor}) {
1160 use Net::Whois::Raw;
1161 use Text::Wrap;
1162
1163# caching useful?
1164#$Net::Whois::Raw::CACHE_DIR = "/var/spool/pwhois/";
1165#$Net::Whois::Raw::CACHE_TIME = 60;
1166
1167 my ($dominfo, $whois_server) = whois($webvar{qfor});
1168##fixme: if we're given an IP, try rwhois as well as whois so we get the real final data
1169
1170 # le sigh. idjits spit out data without linefeeds...
1171 $Text::Wrap::columns = 88;
1172
1173# &%$@%@# high-bit crap. We should probably find a way to properly recode these
1174# instead of one-by-one. Note CGI::Simple's escapeHTML() doesn't do more than
1175# the bare minimum. :/
1176# Mainly an XHTML validation thing.
1177 $dominfo = $q->escapeHTML($dominfo);
1178 $dominfo =~ s/\xa9/\&copy;/g;
1179 $dominfo =~ s/\xae/\&reg;/g;
1180
1181 $page->param(qfor => $webvar{qfor});
1182 $page->param(dominfo => wrap('','',$dominfo));
1183 $page->param(whois_server => $whois_server);
1184 } else {
1185 $page->param(errmsg => "Missing host or domain to query in WHOIS") if $webvar{askaway};
1186 }
1187
1188} elsif ($webvar{page} eq 'log') {
1189
1190##fixme put in some real log-munching stuff
1191##fixme need to add bits to *create* log entries...
1192 my $sql = "SELECT user_id, email, name, entry, date_trunc('second',stamp) FROM log WHERE ";
1193 my $id = $curgroup; # we do this because the group log may be called from (almost) any page,
1194 # but the others are much more limited. this is probably non-optimal.
1195 if ($webvar{ltype} && $webvar{ltype} eq 'user') {
1196 $sql .= "user_id=?";
1197 $id = $webvar{id};
1198 $page->param(logfor => 'user '.userFullName($dbh,$id));
1199 } elsif ($webvar{ltype} && $webvar{ltype} eq 'dom') {
1200 $sql .= "domain_id=?";
1201 $id = $webvar{id};
1202 $page->param(logfor => 'domain '.domainName($dbh,$id));
1203 } else {
1204 # Default to listing curgroup log
1205 $sql .= "group_id=?";
1206 $page->param(logfor => 'group '.groupName($dbh,$id));
1207 }
1208 my $sth = $dbh->prepare($sql);
1209 $sth->execute($id);
1210 my @logbits;
1211 while (my ($uid, $email, $name, $entry, $stamp) = $sth->fetchrow_array) {
1212 my %row;
1213 $row{userfname} = $name;
1214 $row{userid} = $uid;
1215 $row{useremail} = $email;
1216 $row{logentry} = $entry;
1217 ($row{logtime}) = ($stamp =~ /^(.+)-\d\d$/);
1218 push @logbits, \%row;
1219 }
1220 $page->param(logentries => \@logbits);
1221
1222} # end $webvar{page} dance
1223
1224
1225# start output here so we can redirect pages.
1226print "Content-type: text/html\n\n", $header->output;
1227
1228##common bits
1229if ($webvar{page} ne 'login') {
1230 $page->param(username => $session->param("username"));
1231
1232 $page->param(group => $curgroup);
1233 $page->param(groupname => groupName($dbh,$curgroup));
1234 $page->param(logingrp => groupName($dbh,$logingroup));
1235 $page->param(logingrp_num => $logingroup);
1236
1237 $page->param(mayimport => $permissions{admin} || $permissions{domain_create});
1238 $page->param(maybulk => $permissions{admin} || $permissions{domain_edit} || $permissions{domain_create} || $permissions{domain_delete});
1239
1240 # group tree. should go elsewhere, probably
1241 my $tmpgrplist = fill_grptree($logingroup,$curgroup);
1242 $page->param(grptree => $tmpgrplist);
1243 $page->param(subs => ($tmpgrplist ? 1 : 0)); # probably not useful to pass gobs of data in for a boolean
1244 $page->param(inlogingrp => $curgroup == $logingroup);
1245
1246 # stuff for menu group change. nb: this is icky.
1247 fill_grouplist("grouplist");
1248
1249# fill in the URL-to-self
1250 $page->param(whereami => $uri_self);
1251}
1252
1253print "<pre>\n";
1254foreach (@debugbits) { print; }
1255print "</pre>\n";
1256
1257# spit it out
1258print $page->output;
1259
1260if ($debugenv) {
1261 print "<div id=\"debug\">webvar keys: <pre>\n";
1262 foreach my $key (keys %webvar) {
1263 print "key: $key\tval: $webvar{$key}\n";
1264 }
1265 print "</pre>\nsession:\n<pre>\n";
1266 my $sesdata = $session->dataref();
1267 foreach my $key (keys %$sesdata) {
1268 print "key: $key\tval: ".$sesdata->{$key}."\n";
1269 }
1270 print "</pre>\nENV:\n<pre>\n";
1271 foreach my $key (keys %ENV) {
1272 print "key: $key\tval: $ENV{$key}\n";
1273 }
1274 print "</pre></div>\n";
1275}
1276
1277print $footer->output;
1278
1279# as per the docs, Just In Case
1280$session->flush();
1281
1282exit 0;
1283
1284
1285sub fill_grptree {
1286 my $root = shift;
1287 my $cur = shift;
1288 my $indent = shift || ' ';
1289
1290 my @childlist;
1291
1292 my $grptree = HTML::Template->new(filename => 'templates/grptree.tmpl');
1293 getChildren($dbh,$root,\@childlist,'immediate');
1294 return if $#childlist == -1;
1295 my @grouplist;
1296 foreach (@childlist) {
1297 my %row;
1298 $row{grpname} = groupName($dbh,$_);
1299 $row{grpnum} = $_;
1300 $row{whereami} = $uri_self;
1301 # for all that HTML::Template is supposed to keep the HTML out of the Perl, this is so much more compact...
1302# $row{grpdisp} = ($_ == $cur ? "<b>$row{grpname}</b>" : $row{grpname});
1303$row{curgrp} = ($_ == $cur);
1304$row{expanded} = isParent($dbh, $_, 'group', $cur, 'group');
1305$row{expanded} = 1 if $_ == $cur;
1306 $row{subs} = fill_grptree($_,$cur,$indent.' ');
1307 $row{indent} = $indent;
1308 push @grouplist, \%row;
1309 }
1310 $grptree->param(indent => $indent);
1311 $grptree->param(treelvl => \@grouplist);
1312 return $grptree->output;
1313}
1314
1315sub changepage {
1316 my %params = @_; # think this works the way I want...
1317
1318 # handle user check
1319 my $newurl = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?sid=$sid";
1320 foreach (keys %params) {
1321 $newurl .= "&$_=".$q->url_encode($params{$_});
1322 }
1323
1324 # Just In Case
1325 $session->flush();
1326
1327 print "Status: 302\nLocation: $newurl\n\n";
1328 exit;
1329} # end changepage
1330
1331sub fillsoa {
1332 my $def = shift;
1333 my $id = shift;
1334 my $domname = ($def eq 'y' ? '' : "DOMAIN");
1335
1336 $page->param(defrec => $def);
1337
1338# i had a good reason to do this when I wrote it...
1339# $page->param(domain => $domname);
1340# $page->param(group => $DNSDB::group);
1341 $page->param(isgrp => 1) if $def eq 'y';
1342 $page->param(parent => ($def eq 'y' ? groupName($dbh, $DNSDB::group) : domainName($dbh, $id)) );
1343
1344# defaults
1345 $page->param(defcontact => $DNSDB::def{contact});
1346 $page->param(defns => $DNSDB::def{prins});
1347 $page->param(defsoattl => $DNSDB::def{soattl});
1348 $page->param(defrefresh => $DNSDB::def{refresh});
1349 $page->param(defretry => $DNSDB::def{retry});
1350 $page->param(defexpire => $DNSDB::def{expire});
1351 $page->param(defminttl => $DNSDB::def{minttl});
1352
1353 # there are probably better ways to do this. TMTOWTDI.
1354 my %soa = getSOA($dbh,$def,$id);
1355
1356 $page->param(id => $id);
1357 $page->param(recid => $soa{recid});
1358 $page->param(prins => ($soa{prins} ? $soa{prins} : $DNSDB::def{prins}));
1359 $page->param(contact => ($soa{contact} ? $soa{contact} : $DNSDB::def{contact}));
1360 $page->param(refresh => ($soa{refresh} ? $soa{refresh} : $DNSDB::def{refresh}));
1361 $page->param(retry => ($soa{retry} ? $soa{retry} : $DNSDB::def{retry}));
1362 $page->param(expire => ($soa{expire} ? $soa{expire} : $DNSDB::def{expire}));
1363 $page->param(minttl => ($soa{minttl} ? $soa{minttl} : $DNSDB::def{minttl}));
1364 $page->param(ttl => ($soa{ttl} ? $soa{ttl} : $DNSDB::def{soattl}));
1365}
1366
1367sub showdomain {
1368 my $def = shift;
1369 my $id = shift;
1370
1371 # get the SOA first
1372 my %soa = getSOA($dbh,$def,$id);
1373
1374 $page->param(recid => $soa{recid});
1375 $page->param(contact => $soa{contact});
1376 $page->param(prins => $soa{prins});
1377 $page->param(refresh => $soa{refresh});
1378 $page->param(retry => $soa{retry});
1379 $page->param(expire => $soa{expire});
1380 $page->param(minttl => $soa{minttl});
1381 $page->param(ttl => $soa{ttl});
1382
1383 $startwith = $session->param($webvar{page}.'startwith');
1384 $filter = $session->param($webvar{page}.'filter');
1385
1386 my $foo2 = getDomRecs($dbh,$def,$id,$perpage,$webvar{offset},$sortby,$sortorder);
1387
1388 my $row = 0;
1389 foreach my $rec (@$foo2) {
1390 $rec->{type} = $typemap{$rec->{type}};
1391 $rec->{row} = $row % 2;
1392 $rec->{defrec} = $def;
1393 $rec->{sid} = $webvar{sid};
1394 $rec->{id} = $id;
1395 $rec->{distance} = 'n/a' unless ($rec->{type} eq 'MX' || $rec->{type} eq 'SRV');
1396 $rec->{weight} = 'n/a' unless ($rec->{type} eq 'SRV');
1397 $rec->{port} = 'n/a' unless ($rec->{type} eq 'SRV');
1398 $row++;
1399# ACLs
1400 $rec->{record_edit} = ($permissions{admin} || $permissions{record_edit});
1401 $rec->{record_delete} = ($permissions{admin} || $permissions{record_delete});
1402 }
1403 $page->param(reclist => $foo2);
1404}
1405
1406# fill in record type list on add/update/edit record template
1407sub fill_rectypes {
1408 my $type = shift || $reverse_typemap{A};
1409 my $soaflag = shift || 0;
1410
1411 my $sth = $dbh->prepare("SELECT val,name FROM rectypes WHERE stdflag=1 ORDER BY listorder");
1412 $sth->execute;
1413 my @typelist;
1414 while (my ($rval,$rname) = $sth->fetchrow_array()) {
1415 my %row = ( recval => $rval, recname => $rname );
1416 $row{tselect} = 1 if $rval == $type;
1417 push @typelist, \%row;
1418 }
1419 if ($soaflag) {
1420 my %row = ( recval => $reverse_typemap{SOA}, recname => 'SOA' );
1421 $row{tselect} = 1 if $reverse_typemap{SOA} == $type;
1422 push @typelist, \%row;
1423 }
1424 $page->param(typelist => \@typelist);
1425} # fill_rectypes
1426
1427sub fill_recdata {
1428 fill_rectypes($webvar{type});
1429
1430# le sigh. we may get called with many empty %webvar keys
1431 no warnings qw( uninitialized );
1432
1433##todo: allow BIND-style bare names, ASS-U-ME that the name is within the domain?
1434# prefill <domain> or DOMAIN in "Host" space for new records
1435 my $domroot = ($webvar{defrec} eq 'y' ? 'DOMAIN' : domainName($dbh,$webvar{parentid}));
1436 $page->param(name => $domroot);
1437 $page->param(address => $webvar{address});
1438 $page->param(distance => $webvar{distance})
1439 if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV});
1440 $page->param(weight => $webvar{weight}) if $webvar{type} == $reverse_typemap{SRV};
1441 $page->param(port => $webvar{port}) if $webvar{type} == $reverse_typemap{SRV};
1442# retrieve the right ttl instead of falling (way) back to the hardcoded system default
1443 my %soa = getSOA($dbh,$webvar{defrec},$webvar{parentid});
1444 $page->param(ttl => ($webvar{ttl} ? $webvar{ttl} : $soa{minttl}));
1445}
1446
1447sub fill_actypelist {
1448 my $curtype = shift || 'u';
1449
1450 my @actypes;
1451
1452 my %row1 = (actypeval => 'u', actypename => 'user');
1453 $row1{typesel} = 1 if $curtype eq 'u';
1454 push @actypes, \%row1;
1455
1456 my %row2 = (actypeval => 'S', actypename => 'superuser');
1457 $row2{typesel} = 1 if $curtype eq 'S';
1458 push @actypes, \%row2;
1459
1460 $page->param(actypelist => \@actypes);
1461}
1462
1463sub fill_clonemelist {
1464 my $sth = $dbh->prepare("SELECT username,user_id FROM users WHERE group_id=$curgroup");
1465 $sth->execute;
1466
1467 # shut up some warnings, but don't stomp on caller's state
1468 local $webvar{clonesrc} = 0 if !defined($webvar{clonesrc});
1469
1470 my @clonesrc;
1471 while (my ($username,$uid) = $sth->fetchrow_array) {
1472 my %row = (
1473 username => $username,
1474 uid => $uid,
1475 selected => ($webvar{clonesrc} == $uid ? 1 : 0)
1476 );
1477 push @clonesrc, \%row;
1478 }
1479 $page->param(clonesrc => \@clonesrc);
1480}
1481
1482sub fill_fpnla {
1483 my $count = shift;
1484 if ($offset eq 'all') {
1485 $page->param(perpage => $perpage);
1486# uhm....
1487 } else {
1488 # all these bits only have sensible behaviour if offset is numeric. err, probably.
1489 if ($count > $perpage) {
1490 # if there are more results than the default, always show the "all" link
1491 $page->param(navall => 1);
1492
1493 if ($offset > 0) {
1494 $page->param(navfirst => 1);
1495 $page->param(navprev => 1);
1496 $page->param(prevoffs => $offset-1);
1497 }
1498
1499 # show "next" and "last" links if we're not on the last page of results
1500 if ( (($offset+1) * $perpage - $count) < 0 ) {
1501 $page->param(navnext => 1);
1502 $page->param(nextoffs => $offset+1);
1503 $page->param(navlast => 1);
1504 $page->param(lastoffs => int (($count-1)/$perpage));
1505 }
1506 } else {
1507 $page->param(onepage => 1);
1508 }
1509 }
1510} # end fill_fpnla()
1511
1512sub fill_pgcount {
1513 my $pgcount = shift;
1514 my $pgtype = shift;
1515 my $parent = shift;
1516
1517 # Fix display/UI bug where if you are not on the first page of the list, and
1518 # you add a search term or click one of the "starts with" links, you end up
1519 # on a page showing nothing.
1520 # For bonus points, this reverts to the original offset on clicking the "All" link (mostly)
1521 $offset-- while ($offset * $perpage) >= $pgcount;
1522
1523 $page->param(ntot => $pgcount);
1524 $page->param(nfirst => (($offset eq 'all' ? 0 : $offset)*$perpage+1));
1525 $page->param(npglast => ($offset eq 'all' ? $pgcount :
1526 ( (($offset+1)*$perpage) > $pgcount ? $pgcount : (($offset+1)*$perpage) )
1527 ));
1528 $page->param(pgtype => $pgtype);
1529 $page->param(parent => $parent);
1530} # end fill_pgcount()
1531
1532sub listdomains {
1533
1534 $startwith = $session->param($webvar{page}.'startwith');
1535 $filter = $session->param($webvar{page}.'filter');
1536 $searchsubs = $session->param($webvar{page}.'searchsubs');
1537
1538# ACLs
1539 $page->param(domain_create => ($permissions{admin} || $permissions{domain_create}) );
1540 $page->param(domain_edit => ($permissions{admin} || $permissions{domain_edit}) );
1541 $page->param(domain_delete => ($permissions{admin} || $permissions{domain_delete}) );
1542
1543##fixme: $logingroup or $curgroup?
1544 my @childgroups;
1545 getChildren($dbh, $curgroup, \@childgroups, 'all') if $searchsubs;
1546 my $childlist = join(',',@childgroups);
1547
1548 my $sql = "SELECT count(*) FROM domains WHERE group_id IN ($curgroup".($childlist ? ",$childlist" : '').")".
1549 ($startwith ? " AND domain ~* '^[$startwith]'" : '').
1550 ($filter ? " AND domain ~* '$filter'" : '');
1551 my $sth = $dbh->prepare($sql);
1552 $sth->execute;
1553 my ($count) = $sth->fetchrow_array;
1554
1555# fill page count and first-previous-next-last-all bits
1556 fill_pgcount($count,"domains",groupName($dbh,$curgroup));
1557 fill_fpnla($count);
1558
1559# sort/order
1560 $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
1561 $session->param($webvar{page}.'order', $webvar{order}) if $webvar{order};
1562
1563 $sortby = $session->param($webvar{page}.'sortby');
1564 $sortorder = $session->param($webvar{page}.'order');
1565
1566# set up the headers
1567 my @cols = ('domain', 'status', 'group');
1568 my %colheads = (domain => 'Domain', status => 'Status', group => 'Group');
1569 fill_colheads($sortby, $sortorder, \@cols, \%colheads);
1570
1571# $page->param(sortorder => $sortorder);
1572 # hack! hack! pthbttt. have to rethink the status column storage,
1573 # or inactive comes "before" active. *sigh*
1574 $sortorder = ($sortorder eq 'ASC' ? 'DESC' : 'ASC') if $sortby eq 'status';
1575
1576# waffle, waffle - keep state on these as well as sortby, sortorder?
1577 $page->param("start$startwith" => 1) if $startwith && $startwith =~ /^(?:[a-z]|0-9)$/;
1578
1579 $page->param(filter => $filter) if $filter;
1580 $page->param(searchsubs => $searchsubs) if $searchsubs;
1581
1582##fixme
1583##fixme push the SQL and direct database fiddling off into a sub in DNSDB.pm
1584##fixme
1585
1586 $page->param(group => $curgroup);
1587 my @domlist;
1588 $sql = "SELECT domain_id,domain,status,groups.group_name AS group FROM domains".
1589 " INNER JOIN groups ON domains.group_id=groups.group_id".
1590 " WHERE domains.group_id IN ($curgroup".($childlist ? ",$childlist" : '').")".
1591##fixme: don't do variable subs in SQL, use placeholders and params in ->execute()
1592 ($startwith ? " AND domain ~* '^[$startwith]'" : '').
1593 ($filter ? " AND domain ~* '$filter'" : '').
1594 " ORDER BY ".($sortby eq 'group' ? 'groups.group_name' : $sortby).
1595 " $sortorder ".($offset eq 'all' ? '' : " LIMIT $perpage OFFSET ".$offset*$perpage);
1596 $sth = $dbh->prepare($sql);
1597 $sth->execute;
1598 my $rownum = 0;
1599 while (my @data = $sth->fetchrow_array) {
1600 my %row;
1601 $row{domainid} = $data[0];
1602 $row{domain} = $data[1];
1603 $row{status} = ($data[2] ? 'Active' : 'Inactive');
1604 $row{group} = $data[3];
1605 $row{bg} = ($rownum++)%2;
1606# $row{mkactive} = ($data[2] eq 'inactive' ? 1 : 0);
1607 $row{mkactive} = !$data[2];
1608 $row{sid} = $sid;
1609 $row{offset} = $offset;
1610# ACLs
1611 $row{domain_edit} = ($permissions{admin} || $permissions{domain_edit});
1612 $row{domain_delete} = ($permissions{admin} || $permissions{domain_delete});
1613##fixme: need to clean up status indicator/usage/inversion
1614 push @domlist, \%row;
1615 }
1616 $page->param(domtable => \@domlist);
1617} # end listdomains()
1618
1619
1620sub listgroups {
1621
1622 my @childgroups;
1623 getChildren($dbh, $logingroup, \@childgroups, 'all') if $searchsubs;
1624 my $childlist = join(',',@childgroups);
1625
1626 my $sql = "SELECT count(*) FROM groups WHERE parent_group_id IN ($logingroup".($childlist ? ",$childlist" : '').")".
1627 ($startwith ? " AND group_name ~* '^[$startwith]'" : '').
1628 ($filter ? " AND group_name ~* '$filter'" : '');
1629 my $sth = $dbh->prepare($sql);
1630
1631 $sth->execute;
1632 my ($count) = ($sth->fetchrow_array);
1633# fill page count and first-previous-next-last-all bits
1634##fixme - hardcoded group bit
1635 fill_pgcount($count,"groups",'');
1636 fill_fpnla($count);
1637
1638 $page->param(gid => $curgroup);
1639
1640# $sortby = 'group';
1641# sort/order
1642 $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
1643 $session->param($webvar{page}.'order', $webvar{order}) if $webvar{order};
1644
1645 $sortby = $session->param($webvar{page}.'sortby');
1646 $sortorder = $session->param($webvar{page}.'order');
1647
1648# set up the headers
1649 my @cols = ('group','parent','nusers','ndomains');
1650 my %colnames = (group => 'Group', parent => 'Parent Group', nusers => 'Users', ndomains => 'Domains');
1651 fill_colheads($sortby, $sortorder, \@cols, \%colnames);
1652
1653# waffle, waffle - keep state on these as well as sortby, sortorder?
1654 $page->param("start$startwith" => 1) if $startwith && $startwith =~ /^(?:[a-z]|0-9)$/;
1655
1656 $page->param(filter => $filter) if $filter;
1657 $page->param(searchsubs => $searchsubs) if $searchsubs;
1658
1659# munge sortby for columns in database
1660 $sortby = 'g.group_name' if $sortby eq 'group';
1661 $sortby = 'g2.group_name' if $sortby eq 'parent';
1662
1663 my @grouplist;
1664 $sth = $dbh->prepare("SELECT g.group_id, g.group_name, g2.group_name, ".
1665 "count(distinct(u.username)) AS nusers, count(distinct(d.domain)) AS ndomains ".
1666 "FROM groups g ".
1667 "INNER JOIN groups g2 ON g2.group_id=g.parent_group_id ".
1668 "LEFT OUTER JOIN users u ON u.group_id=g.group_id ".
1669 "LEFT OUTER JOIN domains d ON d.group_id=g.group_id ".
1670 "WHERE g.parent_group_id IN ($logingroup".($childlist ? ",$childlist" : '').") ".
1671##fixme: don't do variable subs in SQL, use placeholders and params in ->execute()
1672 ($startwith ? " AND g.group_name ~* '^[$startwith]'" : '').
1673 ($filter ? " AND g.group_name ~* '$filter'" : '').
1674 " GROUP BY g.group_id, g.group_name, g2.group_name ".
1675 " ORDER BY $sortby $sortorder ".
1676 ($offset eq 'all' ? '' : " LIMIT $perpage OFFSET ".$offset*$perpage));
1677 $sth->execute;
1678
1679 my $rownum = 0;
1680 while (my @data = $sth->fetchrow_array) {
1681 my %row;
1682 $row{groupid} = $data[0];
1683 $row{groupname} = $data[1];
1684 $row{pgroup} = $data[2];
1685 $row{nusers} = $data[3];
1686 $row{ndomains} = $data[4];
1687 $row{bg} = ($rownum++)%2;
1688 $row{sid} = $sid;
1689 push @grouplist, \%row;
1690 }
1691 $page->param(grouptable => \@grouplist);
1692} # end listgroups()
1693
1694
1695sub fill_grouplist {
1696 my $template_var = shift;
1697 my $cur = shift || $curgroup;
1698
1699 my @childgroups;
1700 getChildren($dbh, $logingroup, \@childgroups, 'all');
1701 my $childlist = join(',',@childgroups);
1702
1703##fixme: need to reorder list so that we can display a pseudotree in group dropdowns
1704
1705 # weesa gonna discard parent_group_id for now
1706 my $sth = $dbh->prepare("SELECT group_id,parent_group_id,group_name FROM groups ".
1707 "WHERE group_id IN ($logingroup".($childlist ? ",$childlist" : '').")".
1708 "ORDER BY group_id");
1709 $sth->execute;
1710 my @grouplist;
1711 while (my ($groupid,$pargroup,$groupname) = $sth->fetchrow_array()) {
1712 my %row;
1713 $row{groupname} = $groupname;
1714 $row{groupval} = $groupid;
1715##fixme: need magic
1716## ... WTF?
1717# $row{defgroup} = '';
1718 $row{groupactive} = 1 if $groupid == $cur;
1719 push @grouplist, \%row;
1720 }
1721
1722 $page->param("$template_var" => \@grouplist);
1723
1724} # end fill_grouplist()
1725
1726
1727sub list_users {
1728
1729 my @childgroups;
1730 getChildren($dbh, $curgroup, \@childgroups, 'all') if $searchsubs;
1731 my $childlist = join(',',@childgroups);
1732
1733 my $sql = "SELECT count(*) FROM users WHERE group_id IN ($curgroup".($childlist ? ",$childlist" : '').")".
1734 ($startwith ? " AND username ~* '^[$startwith]'" : '').
1735 ($filter ? " AND username ~* '$filter'" : '');
1736 my $sth = $dbh->prepare($sql);
1737 $sth->execute;
1738 my ($count) = ($sth->fetchrow_array);
1739
1740# fill page count and first-previous-next-last-all bits
1741##fixme - hardcoded group bit
1742 fill_pgcount($count,"users",'');
1743 fill_fpnla($count);
1744
1745# $sortby = 'user';
1746# sort/order
1747 $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
1748 $session->param($webvar{page}.'order', $webvar{order}) if $webvar{order};
1749
1750 $sortby = $session->param($webvar{page}.'sortby');
1751 $sortorder = $session->param($webvar{page}.'order');
1752
1753# set up the headers
1754 my @cols = ('user','fname','type','group','status');
1755 my %colnames = (user => 'Username', fname => 'Full Name', type => 'Type', group => 'Group', status => 'Status');
1756 fill_colheads($sortby, $sortorder, \@cols, \%colnames);
1757
1758# waffle, waffle - keep state on these as well as sortby, sortorder?
1759 $page->param("start$startwith" => 1) if $startwith && $startwith =~ /^(?:[a-z]|0-9)$/;
1760
1761 $page->param(filter => $filter) if $filter;
1762 $page->param(searchsubs => $searchsubs) if $searchsubs;
1763
1764# munge sortby for columns in database
1765 $sortby = 'u.username' if $sortby eq 'user';
1766 $sortby = 'u.type' if $sortby eq 'type';
1767 $sortby = 'g.group_name' if $sortby eq 'group';
1768 $sortby = 'u.status' if $sortby eq 'status';
1769
1770 my @userlist;
1771 $sql = "SELECT u.user_id, u.username, u.firstname || ' ' || u.lastname AS fname, u.type, g.group_name, u.status ".
1772 "FROM users u ".
1773 "INNER JOIN groups g ON u.group_id=g.group_id ".
1774 "WHERE u.group_id IN ($curgroup".($childlist ? ",$childlist" : '').")".
1775##fixme: don't do variable subs in SQL, use placeholders and params in ->execute()
1776 ($startwith ? " AND u.username ~* '^[$startwith]'" : '').
1777 ($filter ? " AND u.username ~* '$filter'" : '').
1778 " ORDER BY $sortby $sortorder ".
1779 ($offset eq 'all' ? '' : " LIMIT $perpage OFFSET ".$offset*$perpage);
1780
1781 $sth = $dbh->prepare($sql);
1782 $sth->execute;
1783
1784 my $rownum = 0;
1785 while (my @data = $sth->fetchrow_array) {
1786 no warnings "uninitialized"; # Just In Case something stupid happens and a user gets no first or last name
1787 my %row;
1788 $row{userid} = $data[0];
1789 $row{username} = $data[1];
1790 $row{userfull} = $data[2];
1791 $row{usertype} = ($data[3] eq 'S' ? 'superuser' : "user");
1792 $row{usergroup} = $data[4];
1793 $row{active} = $data[5];
1794 $row{bg} = ($rownum++)%2;
1795 $row{sid} = $sid;
1796 push @userlist, \%row;
1797 }
1798 $page->param(usertable => \@userlist);
1799} # end list_users()
1800
1801
1802# Generate all of the glop necessary to add or not the appropriate marker/flag for
1803# the sort order and column in domain, user, group, and record lists
1804# Takes an array ref and hash ref
1805sub fill_colheads {
1806 my $sortby = shift;
1807 my $sortorder = shift;
1808 my $cols = shift;
1809 my $colnames = shift;
1810 my $custom = shift;
1811
1812 my @headings;
1813
1814 foreach my $col (@$cols) {
1815 my %coldata;
1816 $coldata{firstcol} = 1 if $col eq $cols->[0];
1817 $coldata{sid} = $sid;
1818 $coldata{page} = $webvar{page};
1819 $coldata{offset} = $webvar{offset} if $webvar{offset};
1820 $coldata{sortby} = $col;
1821 $coldata{colname} = $colnames->{$col};
1822 if ($col eq $sortby) {
1823 $coldata{order} = ($sortorder eq 'ASC' ? 'DESC' : 'ASC');
1824 $coldata{sortorder} = $sortorder;
1825 } else {
1826 $coldata{order} = 'ASC';
1827 }
1828 if ($custom) {
1829 foreach my $ckey (keys %$custom) {
1830 $coldata{$ckey} = $custom->{$ckey};
1831 }
1832 }
1833 push @headings, \%coldata;
1834 }
1835
1836 $page->param(colheads => \@headings);
1837
1838} # end fill_colheads()
1839
1840
1841sub logaction {
1842 my $domid = shift;
1843 my $username = shift;
1844 my $groupid = shift;
1845 my $entry = shift;
1846
1847##fixme: push SQL into DNSDB.pm
1848##fixme: add bits to retrieve group/domain name info to retain after entity is deleted?
1849 my $sth = $dbh->prepare("SELECT user_id, firstname || ' ' || lastname FROM users WHERE username=?");
1850 $sth->execute($username);
1851 my ($user_id, $fullname) = $sth->fetchrow_array;
1852
1853 $sth = $dbh->prepare("INSERT INTO log (domain_id,user_id,group_id,email,name,entry) ".
1854 "VALUES (?,?,?,?,?,?)") or warn $dbh->errstr;
1855 $sth->execute($domid,$user_id,$groupid,$username,$fullname,$entry) or warn $sth->errstr;
1856} # end logaction()
1857
1858
1859##fixme: generalize to return appropriate id on all cases (ie, use $partype)
1860sub parentID {
1861 my $id = shift;
1862 my $idtype = shift;
1863 my $partype = shift;
1864 my $defrec = shift || '';
1865
1866 my $sql = '';
1867
1868 if ($idtype eq 'dom') {
1869 return $id if $defrec eq 'y'; # "domain" + default records, we're really looking at a group.
1870 $sql = "SELECT group_id FROM domains WHERE domain_id=?";
1871 } elsif ($idtype eq 'rec') {
1872 if ($defrec eq 'y') {
1873 $sql = "SELECT group_id FROM default_records WHERE record_id=?";
1874 } else {
1875 return
1876 $sql = "SELECT d.group_id FROM domains d".
1877 " INNER JOIN records r ON d.domain_id=r.domain_id".
1878 " WHERE r.record_id=?";
1879 }
1880 } elsif ($idtype eq 'group') {
1881 $sql = "SELECT parent_group_id FROM groups WHERE group_id=?";
1882 } elsif ($idtype eq 'user') {
1883 $sql = "SELECT group_id FROM users WHERE user_id=?";
1884 } else {
1885 return "FOO", "BAR"; # can't get here.... we think.
1886 }
1887 my $sth = $dbh->prepare($sql);
1888 $sth->execute($id);
1889 my ($retid) = $sth->fetchrow_array;
1890 return $retid if $retid;
1891 # ahh! fall of the edge of the world if things went sideways
1892 ##fixme: really need to do a little more error handling, I think
1893} # end parentID()
1894
1895
1896# we have to do this in a variety of places; let's make it consistent
1897sub fill_permissions {
1898 my $template = shift; # may need to do several sets on a single page
1899 my $permset = shift; # hashref to permissions on object
1900 my $usercan = shift || \%permissions; # allow alternate user-is-allowed permission block
1901
1902 foreach (@permtypes) {
1903 $template->param("may_$_" => ($usercan->{admin} || $usercan->{$_}));
1904 $template->param($_ => $permset->{$_});
1905 }
1906}
Note: See TracBrowser for help on using the repository browser.