Gro├čes Update:
[ipfire-2.x.git] / html / cgi-bin / base.cgi
1 #!/usr/bin/perl
2 #
3 # IPFire CGI's - base.cgi
4 #
5 # This code is distributed under the terms of the GPL
6 #
7 # (c) place a name here
8 #
9 # $Id: base.cgi,v 1.1.2.10 2005/11/03 19:20:50 franck78 Exp $
10 #
11 #
12
13
14 # This file is a starting base for writting a new GUI screen using the three box model
15 #       Box 1 : global settings for the application
16 #       Box 2 : line editor for multiple data line
17 #       Box 3 : the list of data line, with edit/remove buttons
18 #
19 #       This example do the following
20 #       Read global settings:
21 #               a NAME and an interface (IT)
22 #       Lines of data composed of:
23 #               an ipaddress (IP), an enabled/disabled options (CB), a comment (CO)
24 #
25 #
26 # All you need to do is
27 #       replace 'XY' with your app name
28 #       define your global $settings{'var name'}
29 #       define your strings
30 #       write validation code for Settings1 and Settings2
31 #       write HTML box Settings1 and Settings2
32 #       adapt the sort function
33 #       write the correct configuration file
34 #
35 #
36 # to fully troubleshot your code, uncomment diagnostics, Carp and cluck lines
37 # use diagnostics; # need to add the file /usr/lib/perl5/5.8.x/pods/perldiag.pod before to work
38 # next look at /var/log/httpd/error_log , http://www.perl.com/pub/a/2002/05/07/mod_perl.html may help
39 #use warnings;
40 use strict;
41 #use Carp ();
42 #local $SIG{__WARN__} = \&Carp::cluck;
43
44 require '/var/ipfire/general-functions.pl';     # Replace all occurences of </var/ipfire> with CONFIG_ROOT
45                                                 # before updating cvs IPFire file.
46 require "${General::swroot}/lang.pl";
47 require "${General::swroot}/header.pl";
48
49 # Files used
50 our $setting  = "${General::swroot}/XY/settings";               # particular settings
51 my  $datafile = "${General::swroot}/XY/data";                   # repeted settings (multilines)
52 our $conffile = "${General::swroot}/XY/XY.conf";                # Config file for application XY
53
54 # strings to add to languages databases or in addon language file
55 $Lang::tr{'XY title'}     = 'XY service';
56 $Lang::tr{'XY settings'}  = 'XY setup';
57 $Lang::tr{'XY add data'}  = 'add data';
58 $Lang::tr{'XY edit data'} = 'edit data';
59 $Lang::tr{'XY data'}      = 'XY data';
60
61 # informationnal & log strings, no translation required
62 my  $msg_added           = 'XY added';
63 my  $msg_modified        = 'XY modified';
64 my  $msg_deleted         = 'XY removed';
65 my  $msg_datafileerror   = 'XY data file error';
66 our $msg_configfileerror = 'XY configuration file error';
67
68 my %settings=();
69
70 # Settings1
71 $settings{'NAME'} = '';         # a string field than must be 'GOOD' or 'good'
72 $settings{'IT'} = '';           # a 'choose' field for color interface
73 $settings{'TURBO'} = 'off';     # a checkbox field to enable something
74
75 # Settings2 for editing the multi-line list
76 # Must not be saved by writehash !
77 $settings{'IP'} = '';                   # datalines are: IPaddress,enable,comment 
78 $settings{'CB'} = 'off';                # Every check box must be set to off
79 $settings{'COMMENT'} = '';
80 my @nosaved=('IP','CB','COMMENT');        # List here ALL setting2 fields. Mandatory
81
82 $settings{'ACTION'} = '';               # add/edit/remove....
83 $settings{'KEY1'} = '';                 # point record for ACTION
84
85 # Define each field that can be used to sort columns
86 my $sortstring='^IP|^COMMENT';
87 my $errormessage = '';
88 my $warnmessage = '';
89
90 &Header::showhttpheaders();
91
92 # Read needed Ipcop settings (exemple)
93 my %mainsettings=();
94 &General::readhash("${General::swroot}/main/settings", \%mainsettings);
95
96 # Get GUI values
97 &Header::getcgihash(\%settings);
98
99 # Load multiline data. Do it before use in save action
100 our $f = new Multilines (filename => $datafile,
101                          fields   => ['IP','CB','COMMENT'],
102                          comment  => 1
103                         );
104
105 ##
106 ## SAVE Settings1 
107 ##
108 # Remove if no Settings1 needed
109 if ($settings{'ACTION'} eq $Lang::tr{'save'}) {
110
111     #
112     #Validate static Settings1 here
113     #
114     if (($settings{"NAME"} ne "GOOD") &&
115         ($settings{"NAME"} ne "good"))    {
116         $errormessage = 'Enter good or GOOD in Name field';
117     }
118
119     unless ($errormessage) {                                    # Everything is ok, save settings
120         map (delete ($settings{$_}) ,(@nosaved,'ACTION','KEY1'));# Must never be saved
121         &General::writehash($setting, \%settings);              # Save good settings
122         $settings{'ACTION'} = $Lang::tr{'save'};                # Recreate  'ACTION'
123         map ($settings{$_}= '',(@nosaved,'KEY1'));              # and reinit var to empty
124
125         # Rebuild configuration file if needed
126         &BuildConfiguration;
127     }
128
129     ERROR:                                              # Leave the faulty field untouched
130 } else {
131     &General::readhash($setting, \%settings);           # Get saved settings and reset to good if needed
132 }
133
134 ##
135 ## Now manipulate the multiline list with Settings2
136 ##
137
138 # Basic actions are:
139 #       toggle the check box
140 #       add/update a new line
141 #       begin editing a line
142 #       remove a line
143 # $KEY1 contains the index of the line manipulated
144
145 ##
146 ## Toggle CB field.
147 ##
148 if ($settings{'ACTION'} eq $Lang::tr{'toggle enable disable'}) {
149
150     $f->togglebyfields($settings{'KEY1'},'CB');         # toggle checkbox
151     $settings{'KEY1'} = '';                             # End edit mode
152
153     &General::log($msg_modified);
154
155     # save changes
156     $f->savedata || die "$msg_datafileerror";
157
158     # Rebuild configuration file
159     &BuildConfiguration;
160 }
161
162 ##
163 ## ADD/UPDATE a line of configuration from Settings2
164 ##
165 if ($settings{'ACTION'} eq $Lang::tr{'add'}) {
166     # Validate inputs
167     if (! &General::validip($settings{'IP'})) {$errormessage = "Specify an IP value !"};
168     if (! $settings{'COMMENT'} ) {$warnmessage = "no comment specified"};
169
170     unless ($errormessage) {
171         if ($settings{'KEY1'} eq '') { #add or edit ?
172             # insert new data line
173             $f->writedata(-1, $settings{'IP'},$settings{'CB'},$settings{'COMMENT'});
174             &General::log($msg_added);
175         } else {
176             # modify data line
177             $f->writedata($settings{'KEY1'}, $settings{'IP'},$settings{'CB'},$settings{'COMMENT'});
178             $settings{'KEY1'} = '';       # End edit mode
179             &General::log($msg_modified);
180         }
181         # save changes
182         $f->savedata || die "$msg_datafileerror";
183
184         # Rebuild configuration file
185         &BuildConfiguration;
186
187         # if entering data line is a repetitive task, choose here to not erase fields between each addition
188         map ($settings{$_}='' ,@nosaved);
189     }
190 }
191
192 ##
193 ## begin EDIT: move data fields to Settings2 controls
194 ##
195 if ($settings{'ACTION'} eq $Lang::tr{'edit'}) {
196     $f->readdata ($settings{'KEY1'},
197                   $settings{'IP'},
198                   $settings{'CB'},
199                   $settings{'COMMENT'});
200 }
201 ##
202 ## REMOVE: remove selected line
203 ##
204 if ($settings{'ACTION'} eq $Lang::tr{'remove'}) {
205     $f->deleteline ($settings{'KEY1'});
206     $settings{'KEY1'} = '';                             # End remove mode
207     &General::log($msg_deleted);
208
209     # save changes
210     $f->savedata || die "$msg_datafileerror";
211
212     # Rebuild configuration file
213     &BuildConfiguration;
214 }
215
216
217 ##
218 ## Check if sorting is asked
219 ##
220 if ($ENV{'QUERY_STRING'} =~ /$sortstring/ ) {
221     my $newsort=$ENV{'QUERY_STRING'};
222     my $actual=$settings{'SORT_XY'};
223
224     # Reverse actual sort or choose new column ?
225     if ($actual =~ $newsort) {
226         $f->setsortorder ($newsort ,rindex($actual,'Rev'));
227         $newsort .= rindex($actual,'Rev')==-1 ? 'Rev' : '';
228     } else {
229         $f->setsortorder ($newsort ,1);
230     }
231     $f->savedata;                                               # Synchronise file & display
232     $settings{'SORT_XY'} = $newsort;
233     map (delete ($settings{$_}) ,(@nosaved,'ACTION','KEY1'));   # Must never be saved
234     &General::writehash($setting, \%settings);
235     $settings{'ACTION'} = 'SORT';                               # Recreate an 'ACTION'
236     map ($settings{$_}= '',(@nosaved,,'KEY1'));                 # and reinit var to empty
237 }
238
239 ##
240 ## Remove if no Setting1 needed
241 ##
242 if ($settings{'ACTION'} eq '' ) { # First launch from GUI
243     # Place here default value when nothing is initialized
244
245 }
246
247 &Header::openpage($Lang::tr{'XY title'}, 1, '');
248 &Header::openbigbox('100%', 'left', '', $errormessage);
249 my %checked =();     # Checkbox manipulations
250
251 if ($errormessage) {
252     &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
253     print "<font class='base'>$errormessage&nbsp;</font>";
254     &Header::closebox();
255 }
256
257 ##
258 ## First box Settings1. Remove if not needed
259 ##
260 $warnmessage = "<font color=${Header::colourred}><b>$Lang::tr{'capswarning'}</b></font>: $warnmessage" if ($warnmessage);
261
262 &Header::openbox('100%', 'left', $Lang::tr{'XY settings'});
263 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
264 $checked{'IT'}{'RED'} = '';
265 $checked{'IT'}{'GREEN'} = '';
266 $checked{'IT'}{'ORANGE'} = '';
267 $checked{'IT'}{'BLUE'} = '';
268 $checked{'IT'}{$settings{'IT'}} = "checked='checked'";
269 $checked{'TURBO'} = ($settings{'TURBO'} eq 'on') ? "checked='checked'" : '';
270
271 print<<END
272 <table width='100%'>
273 <tr>
274     <td class='base'>Name:</td>
275     <td><input type='text' name='NAME' value='$settings{'NAME'}' /></td>
276     <td align='right'>INTERFACE</td>
277     <td align='right'>red<input type='radio' name='IT' value='RED' $checked{'IT'}{'RED'} /></td>
278 </tr><tr>
279     <td>Turbo:</td>
280     <td><input type='checkbox' name='TURBO' $checked{'TURBO'}' /></td>
281     <td></td>
282     <td align='right'>green<input type='radio' name='IT' value='GREEN' $checked{'IT'}{'GREEN'} /></td>
283 </tr><tr>
284     <td></td>
285     <td></td>
286     <td></td>
287     <td align='right'>blue<input type='radio' name='IT' value='BLUE' $checked{'IT'}{'BLUE'} /></td>
288 </tr><tr>
289     <td></td>
290     <td></td>
291     <td></td>
292     <td align='right'>orange<input type='radio' name='IT' value='ORANGE' $checked{'IT'}{'ORANGE'} /></td>
293 </tr>
294 </table>
295 <br />
296 END
297 ;
298
299 print<<END
300 <table width='100%'>
301 <hr />
302 <tr>
303     <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'this field may be blank'}</td>
304     <td class='base' width='25%'>$warnmessage</td>
305     <td width='50%' align='center'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
306 </tr>
307 </table>
308 </form>
309 END
310 ;
311 &Header::closebox();   # end of Settings1
312
313 ##
314 ## Second box is for editing the an item of the list
315 ##
316 $checked{'CB'} = ($settings{'CB'} eq 'on') ? "checked='checked'" : '';
317
318 my $buttontext = $Lang::tr{'add'};
319 if ($settings{'KEY1'} ne '') {
320     $buttontext = $Lang::tr{'update'};
321     &Header::openbox('100%', 'left', $Lang::tr{'XY edit data'});
322 } else {
323     &Header::openbox('100%', 'left', $Lang::tr{'XY add data'});
324 }
325
326 # Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order'
327 print <<END
328 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
329 <input type='hidden' name='KEY1' value='$settings{'KEY1'}' />
330 <table width='100%'>
331 <tr>
332     <td class='base'>$Lang::tr{'ip address'}:</td>
333     <td><input type='text' name='IP' value='$settings{'IP'}' /></td>
334     <td class='base'>$Lang::tr{'enabled'}</td>
335     <td><input type='checkbox' name='CB' $checked{'CB'} /></td>
336     <td class='base'>$Lang::tr{'remark'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
337     <td><input type 'text' name='COMMENT' value='$settings{'COMMENT'}' /></td>
338 </tr>
339 </table>
340 <hr />
341 <table width='100%'>
342 <tr>
343     <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'this field may be blank'}</td>
344     <td width='50%' align='center'><input type='hidden' name='ACTION' value='$Lang::tr{'add'}' /><input type='submit' name='SUBMIT' value='$buttontext' /></td>
345 </tr>
346 </table>
347 </form>
348 END
349 ;
350 &Header::closebox();
351
352 ##
353 ## Third box shows the list
354 ##
355
356 # Columns headers may be a sort link. In this case it must be named in $sortstring
357 &Header::openbox('100%', 'left', $Lang::tr{'XY data'});
358 print <<END
359 <table width='100%'>
360 <tr>
361     <td width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?IP'><b>$Lang::tr{'ip address'}</b></a></td>
362     <td width='70%' align='center'><a href='$ENV{'SCRIPT_NAME'}?COMMENT'><b>$Lang::tr{'remark'}</b></a></td>
363     <td width='10%' colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
364 </tr>
365 END
366 ;
367
368 ##
369 ## Print each line of @current list
370 ##
371 my $key = 0;
372 $f->readreset; # beginning of data
373 for ($key=0; $key<$f->getnumberofline; $key++) {
374
375     my($cb,$comment,$ip) = $f->readbyfieldsseq($key,'CB','COMMENT','IP');
376
377     #Choose icon for checkbox
378     my $gif = '';
379     my $gdesc = '';
380     if ($cb eq "on") {
381         $gif = 'on.gif';
382         $gdesc = $Lang::tr{'click to disable'};
383     } else {
384         $gif = 'off.gif';
385         $gdesc = $Lang::tr{'click to enable'};
386     }
387
388     #Colorize each line
389     if ($settings{'KEY1'} eq $key) {
390         print "<tr bgcolor='${Header::colouryellow}'>";
391     } elsif ($key % 2) {
392         print "<tr bgcolor='${Header::table2colour}'>";
393     } else {
394         print "<tr bgcolor='${Header::table1colour}'>"; 
395     }
396
397     print <<END
398 <td align='center'>$ip</td>
399 <td align='center'>$comment</td>
400
401 <td align='center'>
402 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
403 <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}' />
404 <input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
405 <input type='hidden' name='KEY1' value='$key' />
406 </form>
407 </td>
408
409 <td align='center'>
410 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
411 <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}' />
412 <input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
413 <input type='hidden' name='KEY1' value='$key' />
414 </form>
415 </td>
416
417 <td align='center'>
418 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
419 <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}' />
420 <input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
421 <input type='hidden' name='KEY1' value='$key' />
422 </form>
423 </td>
424 </tr>
425 END
426 ;
427 } print "</table>";
428
429 # If table contains entries, print 'Key to action icons'
430 if ($key) {
431 print <<END
432 <table>
433 <tr>
434     <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
435     <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
436     <td class='base'>$Lang::tr{'click to disable'}</td>
437     <td>&nbsp;&nbsp;</td>
438     <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
439     <td class='base'>$Lang::tr{'click to enable'}</td>
440     <td>&nbsp;&nbsp;</td>
441     <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
442     <td class='base'>$Lang::tr{'edit'}</td>
443     <td>&nbsp;&nbsp;</td>
444     <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
445     <td class='base'>$Lang::tr{'remove'}</td>
446 </tr>
447 </table>
448 END
449 ;
450 }
451
452 &Header::closebox();
453 &Header::closebigbox();
454 &Header::closepage();
455
456 ## Ouf it's the end !
457
458 ##
459 ## Build the configuration file for application XY
460 ##
461 sub BuildConfiguration {
462     open(FILE, ">/$conffile") or die "$msg_configfileerror";
463     flock(FILE, 2);
464
465     #Global settings
466     print FILE "#\n#  Configuration file for application XY\n#\n\n";
467     print FILE "#     do not edit manually\n";
468     print FILE "#     build for Ipcop:$mainsettings{'HOSTNAME'}\n\n\n";
469     print FILE "service=$settings{'NAME'}\n";
470     print FILE "activate-turbo\n" if $settings{'TURBO'} eq 'on';
471     print FILE "interface=$settings{'IT'}\n\n\n";
472     #write data line
473     {
474         my ($IP,$CB,$COMMENT);
475         $f->readreset;
476         while (defined ($f->readdataseq($IP,$CB,$COMMENT))) {
477             if ($CB eq "on") {
478                 print FILE "$IP\t\t\t\t\t#$COMMENT\n";
479             } else {
480                 print FILE "#DISABLED $IP\t\t\t\t#$COMMENT\n";
481             }
482         }
483     }
484     close FILE;
485
486     # Restart service
487     #system '/usr/local/bin/restartyourhelper';
488 }