Project

General

Profile

Download (23.7 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	Copyright (C) 2010 Ermal Luci <ermal.luci@gmail.com>
4
	Copyright (C) 2010 Scott Ullrich <sullrich@gmail.com>
5
    Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>
6
    All rights reserved.
7
    
8
    Redistribution and use in source and binary forms, with or without
9
    modification, are permitted provided that the following conditions are met:
10
    
11
    1. Redistributions of source code must retain the above copyright notice,
12
       this list of conditions and the following disclaimer.
13
    
14
    2. Redistributions in binary form must reproduce the above copyright
15
       notice, this list of conditions and the following disclaimer in the
16
       documentation and/or other materials provided with the distribution.
17
    
18
    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19
    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
    AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
    POSSIBILITY OF SUCH DAMAGE.
28

    
29
*/
30

    
31
/*
32
	pfSense_BUILDER_BINARIES:	/usr/local/bin/voucher	/usr/local/bin/minicron
33
	pfSense_MODULE:	captiveportal
34
*/
35

    
36
/* include all configuration functions */
37
if(!function_exists('captiveportal_syslog'))
38
	require_once("captiveportal.inc");
39

    
40
function xmlrpc_sync_voucher_expire($vouchers, $syncip, $port, $password, $username) {
41
	global $g, $config;
42
	require_once("xmlrpc.inc");
43
	if($port == "443") 
44
		$url = "https://{$syncip}";
45
	else 
46
		$url = "http://{$syncip}";
47

    
48
	/* Construct code that is run on remote machine */
49
	$method = 'pfsense.exec_php';
50
	$execcmd  = <<<EOF
51
	require_once('/etc/inc/captiveportal.inc');
52
	require_once('/etc/inc/voucher.inc');
53
	voucher_expire(\$vouchers);
54

    
55
EOF;
56

    
57
	/* assemble xmlrpc payload */
58
	$params = array(
59
		XML_RPC_encode($password),
60
		XML_RPC_encode($execcmd)
61
	);
62

    
63
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
64
	$msg = new XML_RPC_Message($method, $params);
65
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
66
	$cli->setCredentials($username, $password);
67
	$resp = $cli->send($msg, "250");
68
	if(!is_object($resp)) {
69
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
70
		log_error($error);
71
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
72
		return false;
73
	} elseif($resp->faultCode()) {
74
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
75
		log_error($error);
76
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
77
		return false;
78
	} else {
79
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
80
	}
81

    
82
	$toreturn =  XML_RPC_Decode($resp->value());
83

    
84
	return $toreturn;
85
}
86

    
87
function xmlrpc_sync_voucher_disconnect($dbent, $syncip, $port, $password, $username, $term_cause = "1", $stop_time = null) {
88
	global $g, $config;
89
	require_once("xmlrpc.inc");
90
	if($port == "443") 
91
		$url = "https://{$syncip}";
92
	else 
93
		$url = "http://{$syncip}";
94

    
95
	/* Construct code that is run on remote machine */
96
	$method = 'pfsense.exec_php';
97
	$execcmd  = <<<EOF
98
	require_once('/etc/inc/captiveportal.inc');
99
	require_once('/etc/inc/voucher.inc');
100
	\$radiusservers = captiveportal_get_radius_servers();
101
	captiveportal_disconnect(\$dbent, \$radiusservers, \$term_cause, \$stop_time);
102

    
103
EOF;
104

    
105
	/* assemble xmlrpc payload */
106
	$params = array(
107
		XML_RPC_encode($password),
108
		XML_RPC_encode($execcmd)
109
	);
110

    
111
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
112
	$msg = new XML_RPC_Message($method, $params);
113
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
114
	$cli->setCredentials($username, $password);
115
	$resp = $cli->send($msg, "250");
116
	if(!is_object($resp)) {
117
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
118
		log_error($error);
119
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
120
		return false;
121
	} elseif($resp->faultCode()) {
122
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
123
		log_error($error);
124
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
125
		return false;
126
	} else {
127
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
128
	}
129

    
130
	$toreturn =  XML_RPC_Decode($resp->value());
131

    
132
	return $toreturn;
133
}
134

    
135
function xmlrpc_sync_used_voucher($voucher_received, $syncip, $port, $password, $username) {
136
	global $g, $config;
137
	require_once("xmlrpc.inc");
138
	if($port == "443") 
139
		$url = "https://{$syncip}";
140
	else 
141
		$url = "http://{$syncip}";
142

    
143
	/* Construct code that is run on remote machine */
144
	$method = 'pfsense.exec_php';
145
	$execcmd  = <<<EOF
146
	require_once('/etc/inc/voucher.inc');
147
	\$timeleft = voucher_auth({$voucher_received});
148
	\$toreturn = array();
149
	\$toreturn['timeleft'] = \$timeleft;
150
	\$toreturn['voucher']['roll'] = \$config['voucher']['roll'];
151

    
152
EOF;
153

    
154
	/* assemble xmlrpc payload */
155
	$params = array(
156
		XML_RPC_encode($password),
157
		XML_RPC_encode($execcmd)
158
	);
159

    
160
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
161
	$msg = new XML_RPC_Message($method, $params);
162
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
163
	$cli->setCredentials($username, $password);
164
	$resp = $cli->send($msg, "250");
165
	if(!is_object($resp)) {
166
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
167
		log_error($error);
168
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
169
		return 0; // $timeleft
170
	} elseif($resp->faultCode()) {
171
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
172
		log_error($error);
173
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
174
		return 0; // $timeleft
175
	} else {
176
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
177
	}
178
	$toreturn =  XML_RPC_Decode($resp->value());
179
	if (is_array($toreturn['voucher']) && (count($toreturn['voucher']['roll']) <> count($config['voucher']['roll']))) {
180
		$config['voucher']['roll'] = $toreturn['voucher']['roll'];
181
		write_config("Captive Portal Voucher database synchronized with {$url}");
182
		voucher_configure(true);
183
	}
184

    
185
	return $toreturn['timeleft'];
186
}
187

    
188
function voucher_expire($voucher_received) {
189
	global $g, $config;
190

    
191
	$voucherlck = lock('voucher', LOCK_EX);
192

    
193
	// XMLRPC Call over to the master Voucher node
194
	if(!empty($config['voucher']['vouchersyncdbip'])) {
195
		$syncip   = $config['voucher']['vouchersyncdbip'];
196
		$syncport = $config['voucher']['vouchersyncport'];
197
		$syncpass = $config['voucher']['vouchersyncpass'];
198
		$vouchersyncusername = $config['voucher']['vouchersyncusername'];
199
		xmlrpc_sync_voucher_expire($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
200
	}
201

    
202
	// read rolls into assoc array with rollid as key and minutes as value
203
	$tickets_per_roll = array();
204
	$minutes_per_roll = array();
205
	if (is_array($config['voucher']['roll'])) {
206
		foreach ($config['voucher']['roll'] as $rollent) {
207
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
208
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
209
		}
210
	}
211

    
212
	// split into an array. Useful for multiple vouchers given
213
	$a_vouchers_received = split("[\t\n\r ]+",$voucher_received); 
214
	$active_dirty = false;
215

    
216
	$cpdb = captiveportal_read_db(false, 4); /* Indexed by Voucher */
217
	$unsetindexes[] = array();
218

    
219
	// go through all received vouchers, check their valid and extract
220
	// Roll# and Ticket# using the external readvoucher binary
221
	foreach ($a_vouchers_received as $voucher) {
222
		$v = escapeshellarg($voucher);
223
		if (strlen($voucher) < 3)
224
			continue;   // seems too short to be a voucher!
225

    
226
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher.cfg -k {$g['varetc_path']}/voucher.public -- $v");
227
		list($status, $roll, $nr) = explode(" ", $result);
228
		if ($status == "OK") {
229
			// check if we have this ticket on a registered roll for this ticket 
230
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
231
				// voucher is from a registered roll. 
232
				if (!isset($active_vouchers[$roll]))
233
					$active_vouchers[$roll] = voucher_read_active_db($roll);
234
				// valid voucher. Store roll# and ticket#
235
				if (!empty($active_vouchers[$roll][$voucher])) {
236
					$active_dirty = true;
237
					unset($active_vouchers[$roll][$voucher]);
238
				}
239
				// check if voucher already marked as used
240
				if (!isset($bitstring[$roll]))
241
					$bitstring[$roll] = voucher_read_used_db($roll);
242
				$pos = $nr >> 3; // divide by 8 -> octet
243
				$mask = 1 << ($nr % 8);
244
				// mark bit for this voucher as used
245
				if (!(ord($bitstring[$roll][$pos]) & $mask))
246
					$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
247
				captiveportal_syslog("{$voucher} ({$roll}/{$nr}) forced to expire");
248

    
249
				/* Check if this voucher has any active sessions */
250
				if (isset($cpdb[$voucher])) {
251
					$cpentry = $cpdb[$voucher];
252
					captiveportal_disconnect($cpentry,null,13);
253
					captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"FORCLY TERMINATING VOUCHER {$voucher} SESSION");
254
					unset($cpdb[$voucher]);
255
					$unsetindexes[] = $cpentry[5];
256
				}
257
			} else
258
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registererd Roll");
259
		} else
260
			// hmm, thats weird ... not what I expected
261
			captiveportal_syslog("$voucher invalid: $result !!");
262
	}
263

    
264
	// Refresh active DBs
265
	if ($active_dirty == true) {
266
		foreach ($active_vouchers as $roll => $active)
267
			voucher_write_active_db($roll, $active);
268
	}
269

    
270
	// Write back the used DB's
271
	if (is_array($bitstring)) {
272
		foreach ($bitstring as $roll => $used) {
273
			if(is_array($used)) {
274
				foreach($used as $u)
275
					voucher_write_used_db($roll, base64_encode($u));
276
			} else {
277
				voucher_write_used_db($roll, base64_encode($used));
278
			}
279
		}
280
	}
281

    
282
	unlock($voucherlck);
283

    
284
	/* Write database */
285
	if (!empty($unsetindexes))
286
		captiveportal_write_db($cpdb, false, $unsetindexes);
287

    
288
	return true;
289
}
290

    
291
/* 
292
 * Authenticate a voucher and return the remaining time credit in minutes
293
 * if $test is set, don't mark the voucher as used nor add it to the list
294
 * of active vouchers
295
 * If $test is set, simply test the voucher. Don't change anything
296
 * but return a more verbose error and result message back
297
 */
298
function voucher_auth($voucher_received, $test = 0) {
299
	global $g, $config;
300

    
301
	$voucherlck = lock('voucher', LOCK_EX);
302

    
303
	// XMLRPC Call over to the master Voucher node
304
	if(!empty($config['voucher']['vouchersyncdbip'])) {
305
		$syncip   = $config['voucher']['vouchersyncdbip'];
306
		$syncport = $config['voucher']['vouchersyncport'];
307
		$syncpass = $config['voucher']['vouchersyncpass'];
308
		$vouchersyncusername = $config['voucher']['vouchersyncusername'];
309
		$remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
310
	}
311

    
312
	// read rolls into assoc array with rollid as key and minutes as value
313
	$tickets_per_roll = array();
314
	$minutes_per_roll = array();
315
	if (is_array($config['voucher']['roll'])) {
316
		foreach ($config['voucher']['roll'] as $rollent) {
317
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
318
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
319
		}
320
	}
321

    
322
	// split into an array. Useful for multiple vouchers given
323
	$a_vouchers_received = split("[\t\n\r ]+",$voucher_received); 
324
	$error = 0;
325
	$test_result = array();     // used to display for voucher test option in GUI
326
	$total_minutes = 0;
327
	$first_voucher = "";
328
	$first_voucher_roll = 0;
329

    
330
	// go through all received vouchers, check their valid and extract
331
	// Roll# and Ticket# using the external readvoucher binary
332
	foreach ($a_vouchers_received as $voucher) {
333
		$v = escapeshellarg($voucher);
334
		if (strlen($voucher) < 3)
335
			continue;   // seems too short to be a voucher!
336

    
337
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher.cfg -k {$g['varetc_path']}/voucher.public -- $v");
338
		list($status, $roll, $nr) = explode(" ", $result);
339
		if ($status == "OK") {
340
			if (!$first_voucher) {
341
				// store first voucher. Thats the one we give the timecredit
342
				$first_voucher = $voucher;
343
				$first_voucher_roll = $roll;
344
			}
345
			// check if we have this ticket on a registered roll for this ticket 
346
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
347
				// voucher is from a registered roll. 
348
				if (!isset($active_vouchers[$roll]))
349
					$active_vouchers[$roll] = voucher_read_active_db($roll);
350
				// valid voucher. Store roll# and ticket#
351
				if (!empty($active_vouchers[$roll][$voucher])) {
352
					list($timestamp,$minutes) = explode(",", $active_vouchers[$roll][$voucher]);
353
					// we have an already active voucher here.
354
					$remaining = intval((($timestamp + (60*$minutes)) - time())/60);
355
					$test_result[] = "$voucher ($roll/$nr) active and good for $remaining Minutes";
356
					$total_minutes += $remaining;
357
				} else {
358
					// voucher not used. Check if ticket Id is on the roll (not too high)
359
					// and if the ticket is marked used.
360
					// check if voucher already marked as used
361
					if (!isset($bitstring[$roll]))
362
						$bitstring[$roll] = voucher_read_used_db($roll);
363
					$pos = $nr >> 3; // divide by 8 -> octet
364
					$mask = 1 << ($nr % 8);
365
					if (ord($bitstring[$roll][$pos]) & $mask) {
366
						$test_result[] = "$voucher ($roll/$nr) already used and expired";
367
						captiveportal_syslog("$voucher ($roll/$nr) already used and expired");
368
						$total_minutes = -1;    // voucher expired
369
						$error++;
370
					} else {
371
						// mark bit for this voucher as used
372
						$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
373
						$test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
374
						$total_minutes += $minutes_per_roll[$roll];
375
					}
376
				}
377
			} else {
378
				$test_result[] = "$voucher ($roll/$nr): not found on any registererd Roll";
379
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registererd Roll");
380
			}
381
		} else {
382
			// hmm, thats weird ... not what I expected
383
			$test_result[] = "$voucher invalid: $result !!";
384
			captiveportal_syslog("$voucher invalid: $result !!");
385
			$error++;
386
		}
387
	}
388

    
389
	// if this was a test call, we're done. Return the result.
390
	if ($test) {
391
		if ($error) {
392
			$test_result[] = "Access denied!";
393
		} else {
394
			$test_result[] = "Access granted for $total_minutes Minutes in total.";
395
		}
396
		unlock($voucherlck);
397

    
398
		return $test_result;
399
	}
400

    
401
	// if we had an error (one of the vouchers is invalid), return 0.
402
	// Discussion: we could return the time remaining for good vouchers, but then
403
	// the user wouldn't know that he used at least one invalid voucher.
404
	if ($error) {
405
		unlock($voucherlck);
406
		if ($total_minutes > 0)     // probably not needed, but want to make sure
407
			$total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
408
		return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
409
	}
410

    
411
	// If we did a XMLRPC sync earlier check the timeleft
412
	if (!empty($config['voucher']['vouchersyncdbip'])) 
413
		if($remote_time_used < $total_minutes) 
414
			$total_minutes = $remote_time_used;
415

    
416
	// All given vouchers were valid and this isn't simply a test.
417
	// Write back the used DB's
418
	if (is_array($bitstring)) {
419
		foreach ($bitstring as $roll => $used) {
420
			if(is_array($used)) {
421
				foreach($used as $u)
422
					voucher_write_used_db($roll, base64_encode($u));
423
			} else {
424
				voucher_write_used_db($roll, base64_encode($used));
425
			}
426
		}
427
	}
428

    
429
	// Active DB: we only add the first voucher if multiple given
430
	// and give that one all the time credit. This allows the user to logout and
431
	// log in later using just the first voucher. It also keeps username limited
432
	// to one voucher and that voucher shows the correct time credit in 'active vouchers'
433
	if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) {
434
		list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]);
435
	} else {
436
		$timestamp = time();    // new voucher
437
		$minutes = $total_minutes;
438
	}
439

    
440
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
441
	voucher_write_active_db($roll, $active_vouchers[$first_voucher_roll]);
442

    
443
	unlock($voucherlck);
444

    
445
	return $total_minutes;
446
}
447

    
448
function voucher_configure($sync = false) {
449
	global $config, $g;
450

    
451
	/* kill any running minicron */
452
	killbypid("{$g['varrun_path']}/vouchercron.pid");
453

    
454
	if (!isset($config['voucher']['enable']))
455
		return 0;
456

    
457
        if ($g['booting'])
458
            echo "Enabling voucher support... ";
459
	if ($sync == true)
460
	    captiveportal_syslog("Writing voucher db from sync data...");
461

    
462
        // start cron if we're asked to save runtime DB periodically
463
        // to XML config if it changed
464
        $croninterval = $config['voucher']['saveinterval'] * 60; // need seconds. Config has minutes
465
        if ($croninterval) {
466
            /* start pruning process (interval defaults to 60 seconds) */
467
            mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/vouchercron.pid " .
468
                    "/etc/rc.savevoucher");
469
        }
470

    
471
	$voucherlck = lock('voucher', LOCK_EX);
472

    
473
        /* write public key used to verify vouchers */
474
        $pubkey = base64_decode($config['voucher']['publickey']);
475
        $fd = fopen("{$g['varetc_path']}/voucher.public", "w");
476
        if (!$fd) {
477
            captiveportal_syslog("Voucher error: cannot write voucher.public\n");
478
	    unlock($voucherlck);
479
            return 1;
480
        }
481
        fwrite($fd, $pubkey);
482
        fclose($fd);
483
        @chmod("{$g['varetc_path']}/voucher.public", 0600);
484

    
485
        /* write config file used by voucher binary to decode vouchers */
486
        $fd = fopen("{$g['varetc_path']}/voucher.cfg", "w");
487
        if (!$fd) {
488
            printf("Error: cannot write voucher.cfg\n");
489
	    unlock($voucherlck);
490
            return 1;
491
        }
492
        fwrite($fd, "{$config['voucher']['rollbits']},{$config['voucher']['ticketbits']},{$config['voucher']['checksumbits']},{$config['voucher']['magic']},{$config['voucher']['charset']}\n");
493
        fclose($fd);
494
        @chmod("{$g['varetc_path']}/voucher.cfg", 0600);
495
	unlock($voucherlck);
496

    
497
        if (($g['booting'] || $sync == true) && is_array($config['voucher']['roll'])) {
498

    
499
		$voucherlck = lock('voucher', LOCK_EX);
500

    
501
            // create active and used DB per roll on ramdisk from config
502
            foreach ($config['voucher']['roll'] as $rollent) {
503

    
504
                $roll = $rollent['number'];
505
                voucher_write_used_db($roll, $rollent['used']);
506
                $minutes = $rollent['minutes'];
507
                $active_vouchers = array();
508
                $a_active = &$rollent['active'];
509
                if (is_array($a_active)) {
510
                    foreach ($a_active as $activent) {
511
                        $voucher = $activent['voucher'];
512
                        $timestamp = $activent['timestamp'];
513
                        $minutes = $activent['minutes'];
514
                        // its tempting to check for expired timestamps, but during
515
                        // bootup, we most likely don't have the correct time time.
516
                        $active_vouchers[$voucher] = "$timestamp,$minutes";
517
                    }
518
                }
519
                voucher_write_active_db($roll, $active_vouchers);
520
            }
521

    
522
		unlock($voucherlck);
523
		if ($g['booting'])
524
		    echo "done\n";
525
        }
526

    
527
	return 0;
528
}
529

    
530
/* write bitstring of used vouchers to ramdisk. 
531
 * Bitstring must already be base64_encoded!
532
 */
533
function voucher_write_used_db($roll, $vdb) {
534
	global $g;
535

    
536
	$fd = fopen("{$g['vardb_path']}/voucher_used_$roll.db", "w");
537
	if ($fd) {
538
		fwrite($fd, $vdb . "\n");
539
		fclose($fd);
540
	} else
541
		voucher_log(LOG_ERR, "cant write {$g['vardb_path']}/voucher_used_$roll.db");
542
}
543

    
544
/* return assoc array of active vouchers with activation timestamp
545
 * voucher is index. 
546
 */
547
function voucher_read_active_db($roll) {
548
	global $g;
549

    
550
	$active = array();
551
	$dirty = 0;
552
	$file = "{$g['vardb_path']}/voucher_active_$roll.db";
553
	if (file_exists($file)) {
554
		$fd = fopen($file, "r");
555
		if ($fd) {
556
			while (!feof($fd)) {
557
				$line = trim(fgets($fd));
558
				if ($line) {
559
					list($voucher,$timestamp,$minutes) = explode(",", $line); // voucher,timestamp
560
					if ((($timestamp + (60*$minutes)) - time()) > 0)
561
						$active[$voucher] = "$timestamp,$minutes";
562
					else
563
						$dirty=1;
564
				}
565
			}
566
			fclose($fd);
567
			if ($dirty) // if we found expired entries, lets save our snapshot
568
				voucher_write_active_db($roll, $active);
569
		}
570
	}
571
	return $active;
572
}
573

    
574
/* store array of active vouchers back to DB */
575
function voucher_write_active_db($roll, $active) {
576
    global $g;
577

    
578
	if (!is_array($active))
579
		return;
580
    $fd = fopen("{$g['vardb_path']}/voucher_active_$roll.db", "w");
581
    if ($fd) {
582
        foreach($active as $voucher => $value)
583
            fwrite($fd, "$voucher,$value\n");
584
        fclose($fd);
585
    }
586
}
587

    
588
/* return how many vouchers are marked used on a roll */
589
function voucher_used_count($roll) {
590
    global $g;
591

    
592
    $bitstring = voucher_read_used_db($roll);
593
    $max = strlen($bitstring) * 8;
594
    $used = 0;
595
    for ($i = 1; $i <= $max; $i++) {
596
        // check if ticket already used or not. 
597
        $pos = $i >> 3;            // divide by 8 -> octet
598
        $mask = 1 << ($i % 8);  // mask to test bit in octet
599
        if (ord($bitstring[$pos]) & $mask)
600
            $used++;
601
    }   
602
    return $used;
603
}
604

    
605
function voucher_read_used_db($roll) {
606
    global $g;
607

    
608
    $vdb = "";
609
    $file = "{$g['vardb_path']}/voucher_used_$roll.db";
610
    if (file_exists($file)) {
611
        $fd = fopen($file, "r");
612
        if ($fd) {
613
            $vdb = trim(fgets($fd));
614
            fclose($fd);
615
        } else {
616
            voucher_log(LOG_ERR, "cant read {$g['vardb_path']}/voucher_used_$roll.db");
617
        }
618
    }
619
    return base64_decode($vdb);
620
}
621

    
622
function voucher_unlink_db($roll) {
623
    global $g;
624
    @unlink("{$g['vardb_path']}/voucher_used_$roll.db");
625
    @unlink("{$g['vardb_path']}/voucher_active_$roll.db");
626
}
627

    
628
/* we share the log with captiveportal for now */
629
function voucher_log($priority, $message) {
630

    
631
    define_syslog_variables();
632
    $message = trim($message);
633
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
634
    syslog($priority, "Voucher: " . $message);
635
    closelog();
636
}
637

    
638
/* Save active and used voucher DB into XML config and write it to flash
639
 * Called during reboot -> system_reboot_cleanup() and minicron
640
 */
641
function voucher_save_db_to_config() {
642
    global $config, $g;
643
    
644
    if (!isset($config['voucher']['enable']) || $config['voucher']['saveinterval'] == 0) 
645
        return;   // no vouchers or don't want to save DB's
646

    
647
    $voucherlck = lock('voucher', LOCK_EX);
648

    
649
    // walk all active rolls and save runtime DB's to flash
650
    $a_roll = &$config['voucher']['roll'];
651
    while (list($key, $value) = each($a_roll)) {
652
        $rollent = &$a_roll[$key];
653
        $roll = $rollent['number'];
654
        $bitmask = voucher_read_used_db($roll);
655
        $rollent['used'] = base64_encode($bitmask);
656
        $active_vouchers = voucher_read_active_db($roll);
657
        $db = array();
658
		$dbi = 1;
659
        foreach($active_vouchers as $voucher => $line) {
660
            list($timestamp,$minutes) = explode(",", $line);
661
            $activent['voucher'] = $voucher;
662
            $activent['timestamp'] = $timestamp;
663
            $activent['minutes'] = $minutes;
664
            $db["v{$dbi}"] = $activent;
665
	    $dbi++;
666
        }
667
        $rollent['active'] = $db;
668
    }
669

    
670
    unlock($voucherlck);
671

    
672
    write_config();
673
    return;
674
}
675

    
676
?>
(51-51/61)