]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - html/cgi-bin/hosts.cgi
Hardcode theme to ipfire
[people/pmueller/ipfire-2.x.git] / html / cgi-bin / hosts.cgi
CommitLineData
ac1cfefa 1#!/usr/bin/perl
70df8302
MT
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
c3c2ae44 5# Copyright (C) 2007-2019 IPFire Team <info@ipfire.org> #
70df8302 6# #
c3c2ae44 7# This program is free software you can redistribute it and/or modify #
70df8302
MT
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
ac1cfefa
MT
21
22use strict;
23
24# enable only the following on debugging purpose
25#use warnings;
26#use CGI::Carp 'fatalsToBrowser';
27
986e08d9 28require '/var/ipfire/general-functions.pl';
ac1cfefa
MT
29require "${General::swroot}/lang.pl";
30require "${General::swroot}/header.pl";
31
32#workaround to suppress a warning when a variable is used only once
33my @dummy = ( ${Header::colouryellow} );
34undef (@dummy);
35
36# Files used
37my $setting = "${General::swroot}/main/settings";
38our $datafile = "${General::swroot}/main/hosts"; #(our: used in subroutine)
39
f2fdd0c1
CS
40my %color = ();
41my %mainsettings = ();
42&General::readhash("${General::swroot}/main/settings", \%mainsettings);
8186b372 43&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
f2fdd0c1 44
ac1cfefa
MT
45our %settings = ();
46#Settings1
47# removed
48
49#Settings2 for editing the multi-line list
50#Must not be saved !
51$settings{'EN'} = ''; # reuse for dummy field in position zero
52$settings{'IP'} = '';
c3c2ae44
PM
53$settings{'HOST'} = '';
54$settings{'DOM'} = '';
55$settings{'PTR'} = '';
56my @nosaved=('EN','IP','HOST','DOM','PTR'); # List here ALL setting2 fields. Mandatory
ac1cfefa
MT
57
58$settings{'ACTION'} = ''; # add/edit/remove
59$settings{'KEY1'} = ''; # point record for ACTION
60
61#Define each field that can be used to sort columns
62my $sortstring='^IP|^HOST|^DOM';
63$settings{'SORT_HOSTSLIST'} = 'HOST';
64my $errormessage = '';
65my $warnmessage = '';
66
67&Header::showhttpheaders();
68
69#Get GUI values
70&Header::getcgihash(\%settings);
71
72# Load multiline data
73our @current = ();
74if (open(FILE, "$datafile")) {
75 @current = <FILE>;
76 close (FILE);
77}
78
79## Settings1 Box not used...
80&General::readhash("${General::swroot}/main/settings", \%settings);
81
c3c2ae44
PM
82# Set PTR to off if filed was not received
83if ($settings{'PTR'} eq '') {
84 $settings{'PTR'} = 'off';
85}
ac1cfefa
MT
86
87## Now manipulate the multi-line list with Settings2
88# Basic actions are:
89# toggle the check box
90# add/update a new line
91# begin editing a line
92# remove a line
93
94
95# Toggle enable/disable field. Field is in second position
96if ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
97 #move out new line
98 chomp(@current[$settings{'KEY1'}]);
99 my @temp = split(/\,/,@current[$settings{'KEY1'}]);
100
101 $temp[0] = $temp[0] ne '' ? '' : 'on'; # Toggle the field
102 @current[$settings{'KEY1'}] = join (',',@temp)."\n";
103 $settings{'KEY1'} = ''; # End edit mode
104
105 &General::log($Lang::tr{'hosts config changed'});
106
107 #Save current
108 open(FILE, ">$datafile") or die 'hosts datafile error';
109 print FILE @current;
110 close(FILE);
111
112 # Rebuild configuration file
113 &BuildConfiguration;
114}
115
116if ($settings{'ACTION'} eq $Lang::tr{'add'}) {
117 # Validate inputs
118 unless(&General::validip($settings{'IP'})) {
119 $errormessage = $Lang::tr{'invalid fixed ip address'};
120 }
121
122 unless(&General::validhostname($settings{'HOST'})) {
123 $errormessage = $Lang::tr{'invalid hostname'};
124 }
125
126 if ($settings{'DOM'} && ! &General::validdomainname($settings{'DOM'})) {
127 $errormessage = $Lang::tr{'invalid domain name'};
128 }
129
ac1cfefa
MT
130 unless ($errormessage) {
131 if ($settings{'KEY1'} eq '') { #add or edit ?
c3c2ae44 132 unshift (@current, "$settings{'EN'},$settings{'IP'},$settings{'HOST'},$settings{'DOM'},$settings{'PTR'}\n");
ac1cfefa
MT
133 &General::log($Lang::tr{'hosts config added'});
134 } else {
c3c2ae44 135 @current[$settings{'KEY1'}] = "$settings{'EN'},$settings{'IP'},$settings{'HOST'},$settings{'DOM'},$settings{'PTR'}\n";
ac1cfefa
MT
136 $settings{'KEY1'} = ''; # End edit mode
137 &General::log($Lang::tr{'hosts config changed'});
138 }
139
140 # Write changes to config file.
141 &SortDataFile; # sort newly added/modified entry
142 &BuildConfiguration; # then re-build new host
143
144 #map ($settings{$_}='' ,@nosaved); # Clear fields
145 }
146}
147
148if ($settings{'ACTION'} eq $Lang::tr{'edit'}) {
149 #move out new line
150 my $line = @current[$settings{'KEY1'}]; # KEY1 is the index in current
151 chomp($line);
152 my @temp = split(/\,/, $line);
153 $settings{'EN'}=$temp[0]; # Prepare the screen for editing
154 $settings{'IP'}=$temp[1];
155 $settings{'HOST'}=$temp[2];
156 $settings{'DOM'}=$temp[3];
c3c2ae44
PM
157 if ($temp[4] eq '') {
158 $settings{'PTR'} = 'on';
159 } else {
160 $settings{'PTR'}=$temp[4];
161 }
ac1cfefa
MT
162}
163
164if ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
165 splice (@current,$settings{'KEY1'},1); # Delete line
166 open(FILE, ">$datafile") or die 'hosts datafile error';
167 print FILE @current;
168 close(FILE);
169 $settings{'KEY1'} = ''; # End remove mode
170 &General::log($Lang::tr{'hosts config changed'});
171
172 &BuildConfiguration; # then re-build conf which use new data
173}
174
175
176
177## Check if sorting is asked
178# If same column clicked, reverse the sort.
179if ($ENV{'QUERY_STRING'} =~ /$sortstring/ ) {
180 my $newsort=$ENV{'QUERY_STRING'};
181 my $actual=$settings{'SORT_HOSTSLIST'};
182 #Reverse actual sort ?
183 if ($actual =~ $newsort) {
184 my $Rev='';
185 if ($actual !~ 'Rev') {
186 $Rev='Rev';
187 }
188 $newsort.=$Rev;
189 }
190 $settings{'SORT_HOSTSLIST'}=$newsort;
191 map (delete ($settings{$_}) ,(@nosaved,'ACTION','KEY1'));# Must never be saved
192 &General::writehash($setting, \%settings);
193 &SortDataFile;
194 $settings{'ACTION'} = 'SORT'; # Create an 'ACTION'
195 map ($settings{$_} = '' ,@nosaved,'KEY1'); # and reinit vars to empty
196}
197
198if ($settings{'ACTION'} eq '' ) { # First launch from GUI
199 # Place here default value when nothing is initialized
200 $settings{'EN'} = 'on';
201 $settings{'DOM'} = $settings{'DOMAINNAME'};
c3c2ae44 202 $settings{'PTR'} = 'on';
ac1cfefa
MT
203}
204
205&Header::openpage($Lang::tr{'hostname'}, 1, '');
206&Header::openbigbox('100%', 'left', '', $errormessage);
207my %checked=(); # Checkbox manipulations
208
209if ($errormessage) {
210 &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
211 print "<font class='base'>$errormessage&nbsp;</font>";
212 &Header::closebox();
213}
214
215#
216# Remove if no Setting1 needed
217#
218#if ($warnmessage) {
219# $warnmessage = "<font color=${Header::colourred}><b>$Lang::tr{'capswarning'}</b></font>: $warnmessage";
220#}
221#&Header::openbox('100%', 'left', $Lang::tr{'settings'});
222#print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
223#print <<END
224#<table width='100%'>
225#<tr>
226# <td class='base'>$Lang::tr{'domain name'} : $settings{'DOMAINNAME'}</td>
227#</table>
228#
229#END
230#;
231#
232#print <<END
233#<table width='100%'>
234#<hr />
235#<tr>
e3edceeb 236# <td class='base' width='25%'><!--<img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'required field'}</td>-->
ac1cfefa
MT
237# <td class='base' width='25%'>$warnmessage</td>
238# <td width='50%' align='center'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' disabled='disabled' /></td>
239#</tr>
240#</table>
241#</form>
242#END
243#;
244#&Header::closebox(); # end of Settings1
245
246
247#
248# Second check box is for editing the list
249#
250$checked{'EN'}{'on'} = ($settings{'EN'} eq '' ) ? '' : "checked='checked'";
c3c2ae44 251$checked{'PTR'}{'on'} = ($settings{'PTR'} eq 'off' ) ? '' : "checked='checked'";
ac1cfefa
MT
252
253my $buttontext = $Lang::tr{'add'};
254if ($settings{'KEY1'} ne '') {
255 $buttontext = $Lang::tr{'update'};
256 &Header::openbox('100%', 'left', $Lang::tr{'edit an existing host'});
257} else {
258 &Header::openbox('100%', 'left', $Lang::tr{'add a host'});
259}
260
261#Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order'
262print <<END
263<form method='post' action='$ENV{'SCRIPT_NAME'}'>
264<input type='hidden' name='KEY1' value='$settings{'KEY1'}' />
265<table width='100%'>
266<tr>
e3edceeb 267 <td class='base'>$Lang::tr{'host ip'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 268 <td><input type='text' name='IP' value='$settings{'IP'}' /></td>
e3edceeb 269 <td class='base'>$Lang::tr{'hostname'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 270 <td><input type='text' name='HOST' value='$settings{'HOST'}' /></td>
c3c2ae44
PM
271</tr>
272<tr>
e3edceeb 273 <td class='base'>$Lang::tr{'domain name'}:</td>
ac1cfefa 274 <td><input type='text' name='DOM' value='$settings{'DOM'}' /></td>
c3c2ae44
PM
275 <td class='base'>$Lang::tr{'generate ptr'}:</td>
276 <td><input type='checkbox' name='PTR' $checked{'PTR'}{'on'} /></td>
277</tr>
278<tr>
279 <td>&nbsp;</td>
280 <td>&nbsp;</td>
ac1cfefa
MT
281 <td class='base'>$Lang::tr{'enabled'}</td>
282 <td><input type='checkbox' name='EN' $checked{'EN'}{'on'} /></td>
283</tr>
284</table>
0a644b4a 285<br>
ac1cfefa
MT
286<hr />
287<table width='100%'>
288<tr>
e3edceeb 289 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'required field'}</td>
0a644b4a 290 <td width='50%' align='right'><input type='hidden' name='ACTION' value='$Lang::tr{'add'}' /><input type='submit' name='SUBMIT' value='$buttontext' /></td>
ac1cfefa
MT
291</tr>
292</table>
293</form>
294END
295;
296&Header::closebox();
297
298#
299# Third box shows the list, in columns
300#
301# Columns headers may content a link. In this case it must be named in $sortstring
302#
303&Header::openbox('100%', 'left', $Lang::tr{'current hosts'});
304print <<END
cff7f5a1 305<table width='100%' class='tbl'>
ac1cfefa 306<tr>
cff7f5a1
AM
307 <th width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?IP'><b>$Lang::tr{'host ip'}</b></a></th>
308 <th width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?HOST'><b>$Lang::tr{'hostname'}</b></a></th>
c3c2ae44
PM
309 <th width='40%' align='center'><a href='$ENV{'SCRIPT_NAME'}?DOM'><b>$Lang::tr{'domain name'}</b></a></th>
310 <th width='10%' align='center' class='boldbase'><b>$Lang::tr{'ptr'}</b></th>
cff7f5a1 311 <th width='10%' colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></th>
ac1cfefa
MT
312</tr>
313END
314;
315
316#
317# Print each line of @current list
318#
cff7f5a1 319my $col="";
ac1cfefa
MT
320my $key = 0;
321foreach my $line (@current) {
322 chomp($line); # remove newline
323 my @temp=split(/\,/,$line);
324 $temp[3] ='' unless defined $temp[3]; # not always populated
325
326 #Choose icon for checkbox
327 my $gif = '';
328 my $gdesc = '';
329 if ($temp[0] ne '' ) {
330 $gif = 'on.gif';
331 $gdesc = $Lang::tr{'click to disable'};
332 } else {
333 $gif = 'off.gif';
334 $gdesc = $Lang::tr{'click to enable'};
335 }
336
c3c2ae44
PM
337 if ($temp[4] eq '' || $temp[4] eq 'on') {
338 $temp[4] = $Lang::tr{'yes'};
339 } else {
340 $temp[4] = $Lang::tr{'no'};
341 }
342
ac1cfefa
MT
343 #Colorize each line
344 if ($settings{'KEY1'} eq $key) {
345 print "<tr bgcolor='${Header::colouryellow}'>";
346 } elsif ($key % 2) {
cff7f5a1
AM
347 print "<tr>";
348 $col="bgcolor='$color{'color20'}'";
ac1cfefa 349 } else {
cff7f5a1
AM
350 print "<tr>";
351 $col="bgcolor='$color{'color22'}'";
ac1cfefa
MT
352 }
353 print <<END
cff7f5a1
AM
354<td align='center' $col>$temp[1]</td>
355<td align='center' $col>$temp[2]</td>
356<td align='center' $col>$temp[3]</td>
c3c2ae44 357<td align='center' $col>$temp[4]</td>
cff7f5a1 358<td align='center' $col>
ac1cfefa
MT
359<form method='post' action='$ENV{'SCRIPT_NAME'}'>
360<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
361<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
362<input type='hidden' name='KEY1' value='$key' />
363</form>
364</td>
365
cff7f5a1 366<td align='center' $col>
ac1cfefa
MT
367<form method='post' action='$ENV{'SCRIPT_NAME'}'>
368<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
369<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
370<input type='hidden' name='KEY1' value='$key' />
371</form>
372</td>
373
cff7f5a1 374<td align='center' $col>
ac1cfefa
MT
375<form method='post' action='$ENV{'SCRIPT_NAME'}'>
376<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}' />
377<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
378<input type='hidden' name='KEY1' value='$key' />
379</form>
380</td>
381</tr>
382END
383;
384 $key++;
385}
386print "</table>";
387
388# If table contains entries, print 'Key to action icons'
389if ($key) {
390print <<END
391<table>
392<tr>
393 <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
394 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
395 <td class='base'>$Lang::tr{'click to disable'}</td>
396 <td>&nbsp;&nbsp;</td>
397 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
398 <td class='base'>$Lang::tr{'click to enable'}</td>
399 <td>&nbsp;&nbsp;</td>
400 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
401 <td class='base'>$Lang::tr{'edit'}</td>
402 <td>&nbsp;&nbsp;</td>
403 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
404 <td class='base'>$Lang::tr{'remove'}</td>
405</tr>
406</table>
407END
408;
409}
410
411&Header::closebox();
412&Header::closebigbox();
413&Header::closepage();
414
415## Ouf it's the end !
416
417# Sort the "current" array according to choices
418sub SortDataFile
419{
420 our %entries = ();
421
422 # Sort pair of record received in $a $b special vars.
423 # When IP is specified use numeric sort else alpha.
424 # If sortname ends with 'Rev', do reverse sort.
425 #
426 sub fixedleasesort {
427 my $qs=''; # The sort field specified minus 'Rev'
428 if (rindex ($settings{'SORT_HOSTSLIST'},'Rev') != -1) {
429 $qs=substr ($settings{'SORT_HOSTSLIST'},0,length($settings{'SORT_HOSTSLIST'})-3);
430 if ($qs eq 'IP') {
431 my @a = split(/\./,$entries{$a}->{$qs});
432 my @b = split(/\./,$entries{$b}->{$qs});
433 ($b[0]<=>$a[0]) ||
434 ($b[1]<=>$a[1]) ||
435 ($b[2]<=>$a[2]) ||
436 ($b[3]<=>$a[3]);
437 } else {
438 $entries{$b}->{$qs} cmp $entries{$a}->{$qs};
439 }
440 } else { #not reverse
441 $qs=$settings{'SORT_HOSTSLIST'};
442 if ($qs eq 'IP') {
443 my @a = split(/\./,$entries{$a}->{$qs});
444 my @b = split(/\./,$entries{$b}->{$qs});
445 ($a[0]<=>$b[0]) ||
446 ($a[1]<=>$b[1]) ||
447 ($a[2]<=>$b[2]) ||
448 ($a[3]<=>$b[3]);
449 } else {
450 $entries{$a}->{$qs} cmp $entries{$b}->{$qs};
451 }
452 }
453 }
454
455 #Use an associative array (%entries)
456 my $key = 0;
457 foreach my $line (@current) {
458 chomp( $line); #remove newline because can be on field 5 or 6 (addition of REMARK)
c3c2ae44 459 my @temp = ( '','','','','');
ac1cfefa
MT
460 @temp = split (',',$line);
461
462 # Build a pair 'Field Name',value for each of the data dataline.
463 # Each SORTABLE field must have is pair.
464 # Other data fields (non sortable) can be grouped in one
465
c3c2ae44 466 my @record = ('KEY',$key++,'EN',$temp[0],'IP',$temp[1],'HOST',$temp[2],'DOM',$temp[3],'PTR',$temp[4]);
ac1cfefa
MT
467 my $record = {}; # create a reference to empty hash
468 %{$record} = @record; # populate that hash with @record
469 $entries{$record->{KEY}} = $record; # add this to a hash of hashes
470 }
471
472 open(FILE, ">$datafile") or die 'hosts datafile error';
473
474 # Each field value is printed , with the newline ! Don't forget separator and order of them.
475 foreach my $entry (sort fixedleasesort keys %entries) {
c3c2ae44 476 print FILE "$entries{$entry}->{EN},$entries{$entry}->{IP},$entries{$entry}->{HOST},$entries{$entry}->{DOM},$entries{$entry}->{PTR}\n";
ac1cfefa
MT
477 }
478
479 close(FILE);
480 # Reload sorted @current
481 open (FILE, "$datafile");
482 @current = <FILE>;
483 close (FILE);
484}
485
486#
487# Build the configuration file
488#
489sub BuildConfiguration {
490 system '/usr/local/bin/rebuildhosts';
d37cdb1b 491 system '/usr/local/bin/unboundctrl reload &>/dev/null';
ac1cfefa 492}