]> git.ipfire.org Git - ipfire-2.x.git/blob - html/cgi-bin/captive.cgi
Captive-Portal: redesign Webinterface
[ipfire-2.x.git] / html / cgi-bin / captive.cgi
1 #!/usr/bin/perl
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2016 IPFire Team <alexander.marx@ipfire.org> #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
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 ###############################################################################
21
22 use strict;
23 use HTML::Entities();
24 # enable only the following on debugging purpose
25 #use warnings;
26 #use CGI::Carp 'fatalsToBrowser';
27
28 require '/var/ipfire/general-functions.pl';
29 require "${General::swroot}/lang.pl";
30 require "${General::swroot}/header.pl";
31 unless (-e "${General::swroot}/captive/settings") { system("touch ${General::swroot}/captive/settings"); }
32 my %settings=();
33 my %mainsettings;
34 my %color;
35 my %cgiparams=();
36 my %netsettings=();
37 my %checked=();
38 my $errormessage='';
39 my $voucherout="${General::swroot}/captive/voucher_out";
40 my $clients="${General::swroot}/captive/clients";
41 my %voucherhash=();
42 my %clientshash=();
43 my $settingsfile="${General::swroot}/captive/settings";
44
45 unless (-e $voucherout) { system("touch $voucherout"); }
46
47 &Header::getcgihash(\%cgiparams);
48
49 &General::readhash("${General::swroot}/main/settings", \%mainsettings);
50 &General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color);
51 &General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
52 &General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
53
54 &Header::showhttpheaders();
55
56 #actions
57 if ($cgiparams{'ACTION'} eq "$Lang::tr{'save'}"){
58 #saves the Captiveportal settings to disk
59 $settings{'ENABLE_GREEN'} = $cgiparams{'ENABLE_GREEN'};
60 $settings{'ENABLE_BLUE'} = $cgiparams{'ENABLE_BLUE'};
61 $settings{'AUTH'} = $cgiparams{'AUTH'};
62 $settings{'EXPIRE'} = $cgiparams{'EXP_HOUR'}+$cgiparams{'EXP_DAY'}+$cgiparams{'EXP_WEEK'}+$cgiparams{'EXP_MONTH'};
63 $settings{'EXP_HOUR'} = $cgiparams{'EXP_HOUR'};
64 $settings{'EXP_DAY'} = $cgiparams{'EXP_DAY'};
65 $settings{'EXP_WEEK'} = $cgiparams{'EXP_WEEK'};
66 $settings{'EXP_MONTH'} = $cgiparams{'EXP_MONTH'};
67 $settings{'TITLE'} = $cgiparams{'TITLE'};
68 &General::writehash("$settingsfile", \%settings);
69
70 #write Licensetext if defined
71 if ($cgiparams{'AGB'}){
72 $cgiparams{'AGB'} = &Header::escape($cgiparams{'AGB'});
73 open( FH, ">:utf8", "/var/ipfire/captive/agb.txt" ) or die("$!");
74 print FH $cgiparams{'AGB'};
75 close( FH );
76 $cgiparams{'AGB'}="";
77 }
78 #execute binary to reload firewall rules
79 system("/usr/local/bin/captivectrl");
80 }
81
82 if ($cgiparams{'ACTION'} eq "$Lang::tr{'Captive voucherout'}"){
83 #generates a voucher and writes it to /var/ipfire/voucher_out
84
85 #check if we already have a voucher with same code
86 &General::readhasharray("$voucherout", \%voucherhash);
87 foreach my $key (keys %voucherhash) {
88 if($voucherhash{$key}[1] eq $cgiparams{'CODE'}){
89 $errormessage=$Lang::tr{'Captive err doublevoucher'};
90 last;
91 }
92 }
93
94 #check valid remark
95 if ($cgiparams{'REMARK'} ne '' && !&validremark($cgiparams{'REMARK'})){
96 $errormessage=$Lang::tr{'fwhost err remark'};
97 }
98
99 #if no error detected, write to disk
100 if (!$errormessage){
101 my $date=time(); #seconds in utc
102
103 #first get new key from hash
104 my $key=&General::findhasharraykey (\%voucherhash);
105 #initialize all fields with ''
106 foreach my $i (0 .. 3) { $voucherhash{$key}[$i] = "";}
107 #define fields
108 $voucherhash{$key}[0] = $date;
109 $voucherhash{$key}[1] = $cgiparams{'CODE'};
110 $voucherhash{$key}[2] = $settings{'EXPIRE'};
111 $voucherhash{$key}[3] = $cgiparams{'REMARK'};
112 #write values to disk
113 &General::writehasharray("$voucherout", \%voucherhash);
114
115 #now prepare log entry, get expiring date for voucher and decode remark for logfile
116 my $expdate=localtime(time()+$voucherhash{$key}[3]);
117 my $rem=HTML::Entities::decode_entities($voucherhash{$key}[4]);
118
119 #write logfile entry
120 &General::log("Captive", "Generated new voucher $voucherhash{$key}[1] $voucherhash{$key}[2] hours valid expires on $expdate remark $rem");
121 }
122 }
123
124 if ($cgiparams{'ACTION'} eq 'delvoucherout'){
125 #deletes an already generated but unused voucher
126
127 #read all generated vouchers
128 &General::readhasharray("$voucherout", \%voucherhash);
129 foreach my $key (keys %voucherhash) {
130 if($cgiparams{'key'} eq $voucherhash{$key}[0]){
131 #write logenty with decoded remark
132 my $rem=HTML::Entities::decode_entities($voucherhash{$key}[4]);
133 &General::log("Captive", "Delete unused voucher $voucherhash{$key}[1] $voucherhash{$key}[2] hours valid expires on $voucherhash{$key}[3] remark $rem");
134 #delete line from hash
135 delete $voucherhash{$key};
136 last;
137 }
138 }
139 #write back hash
140 &General::writehasharray("$voucherout", \%voucherhash);
141 }
142
143 if ($cgiparams{'ACTION'} eq 'delvoucherinuse'){
144 #delete voucher and connection in use
145
146 #read all active clients
147 &General::readhasharray("$clients", \%clientshash);
148 foreach my $key (keys %clientshash) {
149 if($cgiparams{'key'} eq $clientshash{$key}[0]){
150 #prepare log entry with decoded remark
151 my $rem=HTML::Entities::decode_entities($clientshash{$key}[7]);
152 #write logentry
153 &General::log("Captive", "Delete voucher in use $clientshash{$key}[1] $clientshash{$key}[2] hours valid expires on $clientshash{$key}[3] remark $rem - Connection will be terminated");
154 #delete line from hash
155 delete $clientshash{$key};
156 last;
157 }
158 }
159 #write back hash
160 &General::writehasharray("$clients", \%clientshash);
161 #reload firewallrules to kill connection of client
162 system("/usr/local/bin/captivectrl");
163 }
164
165 #open webpage, print header and open box
166 &Header::openpage($Lang::tr{'Captive menu'}, 1, '');
167 &Header::openbigbox();
168
169 #call error() to see if we have to print an errormessage on website
170 &error();
171
172 #call config() to display the configuration box
173 &config();
174
175 sub getagb(){
176 #open textfile from /var/ipfire/captive/agb.txt
177 open( my $handle, "<:utf8", "/var/ipfire/captive/agb.txt" ) or die("$!");
178 while(<$handle>){
179 #read line by line and print on screen
180 $cgiparams{'AGB'}.= HTML::Entities::decode_entities($_);
181 }
182 close( $handle );
183 }
184
185 sub config(){
186 #prints the config box on the website
187 &Header::openbox('100%', 'left', $Lang::tr{'Captive config'});
188 print <<END
189 <form method='post' action='$ENV{'SCRIPT_NAME'}'>\n
190 <table width='100%' border="0">
191 <tr>
192 END
193 ;
194 #check which parameters have to be enabled (from settings file)
195 $checked{'ENABLE_GREEN'}{'off'} = '';
196 $checked{'ENABLE_GREEN'}{'on'} = '';
197 $checked{'ENABLE_GREEN'}{$settings{'ENABLE_GREEN'}} = "checked='checked'";
198
199 $checked{'ENABLE_BLUE'}{'off'} = '';
200 $checked{'ENABLE_BLUE'}{'on'} = '';
201 $checked{'ENABLE_BLUE'}{$settings{'ENABLE_BLUE'}} = "checked='checked'";
202
203 if ($netsettings{'GREEN_DEV'}){
204 print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourgreen'>Green</font></td><td><input type='checkbox' name='ENABLE_GREEN' $checked{'ENABLE_GREEN'}{'on'} /></td></tr>";
205 }
206 if ($netsettings{'BLUE_DEV'}){
207 print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourblue'>Blue</font></td><td><input type='checkbox' name='ENABLE_BLUE' $checked{'ENABLE_BLUE'}{'on'} /></td></tr>";
208 }
209
210 print<<END
211 </tr>
212 <tr>
213 <td><br>
214 $Lang::tr{'Captive title'}
215 </td>
216 <td><br>
217 <input type='text' name='TITLE' value="$settings{'TITLE'}" size='40'>
218 </td>
219 </tr>
220 END
221 ;
222
223
224 print<<END
225 <tr>
226 <td>
227 $Lang::tr{'Captive authentication'}
228 </td>
229 <td>
230 <select name='AUTH' style='width:8em;'>
231 END
232 ;
233 print "<option value='LICENSE' ";
234 print " selected='selected'" if ($settings{'AUTH'} eq 'LICENSE');
235 print ">$Lang::tr{'Captive auth_lic'}</option>";
236
237 print "<option value='VOUCHER' ";
238 print " selected='selected'" if ($settings{'AUTH'} eq 'VOUCHER');
239 print ">$Lang::tr{'Captive auth_vou'}</option>";
240
241 print<<END
242 </select>
243 </td>
244 </tr>
245 END
246 ;
247
248 if($settings{'AUTH'} eq 'LICENSE'){
249 &agbbox();
250 }
251
252 print"<tr><td>$Lang::tr{'Captive vouchervalid'}</td><td>";
253
254 print "<br><table border='0' with=100%>";
255 print "<th>Stunden</th><th>Tage</th><th>Wochen</th><th>Monate</th>";
256
257 #print hour-dropdownbox
258 my $hrs=3600;
259 print "<tr><td><select name='EXP_HOUR' style='width:8em;'>";
260 print "<option value='0' ";
261 print " selected='selected'" if ($settings{'EXP_HOUR'} eq '0');
262 print ">--</option>";
263 for (my $i = 1; $i<25; $i++){
264 my $exp_sec = $i * $hrs;
265 print "<option value='$exp_sec' ";
266 print " selected='selected'" if ($settings{'EXP_HOUR'} eq $exp_sec);
267 print ">$i</option>";
268 }
269 print "</td><td>";
270
271 #print day-dropdownbox
272 my $days=3600*24;
273 print "<select name='EXP_DAY' style='width:8em;'>";
274 print "<option value='0' ";
275 print " selected='selected'" if ($settings{'EXP_DAY'} eq '0');
276 print ">--</option>";
277 for (my $i = 1; $i<8; $i++){
278 my $exp_sec = $i * $days;
279 print "<option value='$exp_sec' ";
280 print " selected='selected'" if ($settings{'EXP_DAY'} eq $exp_sec);
281 print ">$i</option>";
282 }
283 print "</td><td>";
284
285 #print week-dropdownbox
286 my $week=3600*24*7;
287 print "<select name='EXP_WEEK' style='width:8em;'>";
288 print "<option value='0' ";
289 print " selected='selected'" if ($settings{'EXP_WEEK'} eq '0');
290 print ">--</option>";
291 for (my $i = 1; $i<5; $i++){
292 my $exp_sec = $i * $week;
293 print "<option value='$exp_sec' ";
294 print " selected='selected'" if ($settings{'EXP_WEEK'} eq $exp_sec);
295 print ">$i</option>";
296 }
297 print "</td><td>";
298
299 #print month-dropdownbox
300 my $month=3600*24*30;
301 print "<select name='EXP_MONTH' style='width:8em;'>";
302 print "<option value='0' ";
303 print " selected='selected'" if ($settings{'EXP_MONTH'} eq '0');
304 print ">--</option>";
305 for (my $i = 1; $i<13; $i++){
306 my $exp_sec = $i * $month;
307 print "<option value='$exp_sec' ";
308 print " selected='selected'" if ($settings{'EXP_MONTH'} eq $exp_sec);
309 print ">$i</option>";
310 }
311 print "</td>";
312
313 print "<td>&nbsp;&nbsp;&nbsp;<input type='checkbox' name='unlimited'></td><td>&nbsp;<b>unlimited</b></td>";
314
315 print "</tr></table>";
316
317 print<<END
318 <tr>
319 <td>
320 </td>
321 <td align='right'>
322 <input type='submit' name='ACTION' value="$Lang::tr{'save'}"/>
323 </td>
324 </tr>
325 </table>
326 <br><br>
327 END
328 ;
329 print "</form>";
330
331 &Header::closebox();
332
333 #if settings is set to use vouchers, the voucher part has to be displayed
334 if ($settings{'AUTH'} eq 'VOUCHER'){
335 &voucher();
336 }else{
337 #otherwise we show the licensepart
338 &show_license_connections();
339 }
340 }
341
342 sub agbbox(){
343 &getagb();
344 print<<END
345 <tr>
346 <td>
347 License agreement
348 </td>
349 <td>
350 <br>
351 <textarea cols="50" rows="10" name="AGB">$cgiparams{'AGB'}</textarea>
352 </td>
353 </tr>
354 END
355 ;
356 }
357
358 sub gencode(){
359 #generate a random code only letters from A-Z except 'O' and 0-9
360 my @chars = ("A".."N", "P".."Z", "0".."9");
361 my $randomstring;
362 $randomstring .= $chars[rand @chars] for 1..8;
363 return $randomstring;
364 }
365
366 sub voucher(){
367 #show voucher part
368 #calculate expiredate
369 my $expire = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1] }->(localtime(time()+$settings{'EXPIRE'}));
370
371 &Header::openbox('100%', 'left', $Lang::tr{'Captive voucher'});
372 print<<END
373 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
374 <table class='tbl'>
375 <tr>
376 <th align='center' width='20%'>$Lang::tr{'Captive voucher'}</th><th th align='center' width='25%'>$Lang::tr{'Captive expire'}</th><th align='center' width='55%'>$Lang::tr{'remark'}</th></tr>
377 END
378 ;
379
380 $cgiparams{'CODE'} = &gencode();
381 print "<tr><td><center><b><font size='5'>$cgiparams{'CODE'}</font></b></center></td><td><center><font size='3'>$expire</font></center></td><td><input type='text' name='REMARK' align='left' size='80'></td></tr>";
382 print "</table><br>";
383 print "<center><input type='submit' name='ACTION' value='$Lang::tr{'Captive voucherout'}'><input type='hidden' name='CODE' value='$cgiparams{'CODE'}'</center></form>";
384 &Header::closebox();
385 if (! -z $voucherout) { &show_voucher_out();}
386 if (! -z $clients) { &show_voucher_in_use();}
387 }
388
389 sub show_license_connections(){
390 #if there are active clients, show the box with active connections
391 return if ( -z $clients || ! -f $clients );
392 my $count=0;
393 my $col;
394 &Header::openbox('100%', 'left', $Lang::tr{'Captive voactive'});
395 print<<END
396 <center><table class='tbl'>
397 <tr>
398 <th align='center' width='15%'><font size='1'>$Lang::tr{'Captive mac'}</th><th align='center' width='15%'>$Lang::tr{'Captive ip'}</th><th align='center' width='15%'>$Lang::tr{'Captive voucher'}</th><th th align='center' width='15%'>$Lang::tr{'Captive activated'}</th><th th align='center' width='15%'>$Lang::tr{'Captive expire'}</th><th th align='center' width='15%'>$Lang::tr{'delete'}</th></tr>
399 END
400 ;
401 #read all clients from hash and show table
402 &General::readhasharray("$clients", \%clientshash);
403 foreach my $key (keys %clientshash){
404 my ($sec, $min, $hour, $mday, $mon, $year) = localtime($clientshash{$key}[6]);
405 my ($secx,$minx,$hourx) = localtime($clientshash{$key}[6]+($clientshash{$key}[5]*3600));
406 $mon = '0'.++$mon if $mon<10;
407 $min = '0'.$min if $min<10;
408 $hour = '0'.$hour if $hour<10;
409 $year=$year+1900;
410 if ($count % 2){
411 print" <tr>";
412 $col="bgcolor='$color{'color20'}'";
413 }else{
414 $col="bgcolor='$color{'color22'}'";
415 print" <tr>";
416 }
417 print "<td $col><center>$clientshash{$key}[0]</td><td $col><center>$clientshash{$key}[1]</td><td $col><center>$clientshash{$key}[4]</td><td $col><center>$mday.$mon.$year ";
418 printf("%02d",$hour);
419 print ":";
420 printf("%02d",$min);
421 print "</center></td><td $col><center>$mday.$mon.$year ";
422 printf("%02d",$hourx);
423 print ":";
424 printf("%02d",$minx);
425 print "</td><td $col><form method='post'><center><input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' /><form method='post'><input type='hidden' name='ACTION' value='delvoucherinuse' /><input type='hidden' name='key' value='$clientshash{$key}[0]' /></form></tr>";
426 $count++;
427 }
428
429 print "</table>";
430 &Header::closebox();
431 }
432
433 sub show_voucher_out(){
434 #if there are already generated but unsused vouchers, print a table
435 return if ( -z $voucherout);
436 my $count=0;
437 my $col;
438 &Header::openbox('100%', 'left', $Lang::tr{'Captive vout'});
439 print<<END
440 <center><table class='tbl' border='0'>
441 <tr>
442 <th align='center' width='15%'><font size='1'>$Lang::tr{'date'}</th><th align='center' width='15%'>$Lang::tr{'Captive voucher'}</th><th th align='center' width='15%'>$Lang::tr{'Captive expire'}</th><th align='center' width='60%'>$Lang::tr{'remark'}</th><th align='center' width='5%'>$Lang::tr{'delete'}</th></tr>
443 END
444 ;
445 &General::readhasharray("$voucherout", \%voucherhash);
446 foreach my $key (keys %voucherhash)
447 {
448 my $starttime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1] }->(localtime($voucherhash{$key}[0]));
449 my $endtime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1] }->(localtime($voucherhash{$key}[0]+$voucherhash{$key}[2]));
450
451 if ($count % 2){
452 print" <tr>";
453 $col="bgcolor='$color{'color20'}'";
454 }else{
455 $col="bgcolor='$color{'color22'}'";
456 print" <tr>";
457 }
458
459 print "<td $col><center>$starttime</td>";
460 print "<td $col><center><b>$voucherhash{$key}[1]</b></td>";
461 print "<td $col><center>$endtime</td>";
462 print "<td $col align='center'>$voucherhash{$key}[3]</td>";
463 print "<td $col><form method='post'><center><input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' /><form method='post'><input type='hidden' name='ACTION' value='delvoucherout' /><input type='hidden' name='key' value='$voucherhash{$key}[0]' /></form></tr>";
464 $count++;
465 }
466
467 print "</table>";
468 &Header::closebox();
469 }
470
471 sub show_voucher_in_use(){
472 #if there are active clients which use vouchers show table
473 return if ( -z $clients || ! -f $clients );
474 my $count=0;
475 my $col;
476 &Header::openbox('100%', 'left', $Lang::tr{'Captive voactive'});
477 print<<END
478 <center><table class='tbl'>
479 <tr>
480 <th align='center' width='15%'><font size='1'>$Lang::tr{'Captive mac'}</th><th align='center' width='15%'>$Lang::tr{'Captive ip'}</th><th align='center' width='15%'>$Lang::tr{'Captive voucher'}</th><th th align='center' width='15%'>$Lang::tr{'Captive activated'}</th><th th align='center' width='15%'>$Lang::tr{'Captive expire'}</th><th th align='center' width='15%'>$Lang::tr{'delete'}</th></tr>
481 END
482 ;
483 &General::readhasharray("$clients", \%clientshash);
484 foreach my $key (keys %clientshash)
485 {
486 my ($sec, $min, $hour, $mday, $mon, $year) = localtime($clientshash{$key}[6]);
487 my ($secx,$minx,$hourx, $mdayx, $monx, $yearx) = localtime($clientshash{$key}[6]+$clientshash{$key}[7]);
488
489 $mon = '0'.++$mon if $mon<10;
490 $min = '0'.$min if $min<10;
491 $hour = '0'.$hour if $hour<10;
492 $year=$year+1900;
493
494 $monx = '0'.++$mon if $mon<10;
495 $minx = '0'.$min if $min<10;
496 $hourx = '0'.$hour if $hour<10;
497 $yearx=$year+1900;
498
499 if ($count % 2){
500 print" <tr>";
501 $col="bgcolor='$color{'color20'}'";
502 }else{
503 $col="bgcolor='$color{'color22'}'";
504 print" <tr>";
505 }
506
507 print "<td $col><center>$clientshash{$key}[0]</td><td $col><center>$clientshash{$key}[1]</td><td $col><center>$clientshash{$key}[4]</td><td $col><center>$mday.$mon.$year ";
508 printf("%02d",$hour);
509 print ":";
510 printf("%02d",$min);
511 print "</center></td><td $col><center>$mdayx.$monx.$yearx $clientshash{$key}[3]";
512 print "</td><td $col><form method='post'><center><input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' /><form method='post'><input type='hidden' name='ACTION' value='delvoucherinuse' /><input type='hidden' name='key' value='$clientshash{$key}[0]' /></form></tr>";
513 $count++;
514 }
515
516 print "</table>";
517 &Header::closebox();
518 }
519
520 sub validremark
521 {
522 # Checks a hostname against RFC1035
523 my $remark = $_[0];
524 # Each part should be at least two characters in length
525 # but no more than 63 characters
526 if (length ($remark) < 1 || length ($remark) > 255) {
527 return 0;}
528 # Only valid characters are a-z, A-Z, 0-9 and -
529 if ($remark !~ /^[a-zäöüA-ZÖÄÜ0-9-.:;\|_()\/\s]*$/) {
530 return 0;}
531 # First character can only be a letter or a digit
532 if (substr ($remark, 0, 1) !~ /^[a-zäöüA-ZÖÄÜ0-9]*$/) {
533 return 0;}
534 # Last character can only be a letter or a digit
535 if (substr ($remark, -1, 1) !~ /^[a-zöäüA-ZÖÄÜ0-9.:;_)]*$/) {
536 return 0;}
537 return 1;
538 }
539
540 sub error{
541 #if an errormessage exits, show a box with errormessage
542 if ($errormessage) {
543 &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
544 print "<class name='base'>$errormessage\n";
545 print "&nbsp;</class>\n";
546 &Header::closebox();
547 }
548 }
549
550 &Header::closebigbox();
551 &Header::closepage();