Project

General

Profile

Download (23.6 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
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
		/* Triger a sync of the vouchers on config */
270
		send_event("service sync vouchers");
271
	}
272

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

    
285
	unlock($voucherlck);
286

    
287
	/* Write database */
288
	if (!empty($unsetindexes))
289
		captiveportal_write_db($cpdb, false, $unsetindexes);
290

    
291
	return true;
292
}
293

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

    
304
	if (!isset($config['voucher']['enable']))
305
		return 0;
306

    
307
	$voucherlck = lock('voucher', LOCK_EX);
308

    
309
	// XMLRPC Call over to the master Voucher node
310
	if(!empty($config['voucher']['vouchersyncdbip'])) {
311
		$syncip   = $config['voucher']['vouchersyncdbip'];
312
		$syncport = $config['voucher']['vouchersyncport'];
313
		$syncpass = $config['voucher']['vouchersyncpass'];
314
		$vouchersyncusername = $config['voucher']['vouchersyncusername'];
315
		$remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
316
	}
317

    
318
	// read rolls into assoc array with rollid as key and minutes as value
319
	$tickets_per_roll = array();
320
	$minutes_per_roll = array();
321
	if (is_array($config['voucher']['roll'])) {
322
		foreach ($config['voucher']['roll'] as $rollent) {
323
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
324
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
325
		}
326
	}
327

    
328
	// split into an array. Useful for multiple vouchers given
329
	$a_vouchers_received = split("[\t\n\r ]+",$voucher_received); 
330
	$error = 0;
331
	$test_result = array();     // used to display for voucher test option in GUI
332
	$total_minutes = 0;
333
	$first_voucher = "";
334
	$first_voucher_roll = 0;
335

    
336
	// go through all received vouchers, check their valid and extract
337
	// Roll# and Ticket# using the external readvoucher binary
338
	foreach ($a_vouchers_received as $voucher) {
339
		$v = escapeshellarg($voucher);
340
		if (strlen($voucher) < 3)
341
			continue;   // seems too short to be a voucher!
342

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

    
395
	// if this was a test call, we're done. Return the result.
396
	if ($test) {
397
		if ($error) {
398
			$test_result[] = gettext("Access denied!");
399
		} else {
400
			$test_result[] = sprintf(gettext("Access granted for %d Minutes in total."),$total_minutes);
401
		}
402
		unlock($voucherlck);
403

    
404
		return $test_result;
405
	}
406

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

    
417
	// If we did a XMLRPC sync earlier check the timeleft
418
	if (!empty($config['voucher']['vouchersyncdbip'])) 
419
		if($remote_time_used < $total_minutes) 
420
			$total_minutes = $remote_time_used;
421

    
422
	// All given vouchers were valid and this isn't simply a test.
423
	// Write back the used DB's
424
	if (is_array($bitstring)) {
425
		foreach ($bitstring as $roll => $used) {
426
			if(is_array($used)) {
427
				foreach($used as $u)
428
					voucher_write_used_db($roll, base64_encode($u));
429
			} else {
430
				voucher_write_used_db($roll, base64_encode($used));
431
			}
432
		}
433
	}
434

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

    
446
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
447
	voucher_write_active_db($roll, $active_vouchers[$first_voucher_roll]);
448

    
449
	/* Triger a sync of the vouchers on config */
450
	send_event("service sync vouchers");
451

    
452
	unlock($voucherlck);
453

    
454
	return $total_minutes;
455
}
456

    
457
function voucher_configure($sync = false) {
458
	global $config, $g;
459

    
460
	if (!isset($config['voucher']['enable']))
461
		return 0;
462

    
463
        if ($g['booting'])
464
            echo "Enabling voucher support... ";
465
	if ($sync == true)
466
	    captiveportal_syslog("Writing voucher db from sync data...");
467

    
468
	$voucherlck = lock('voucher', LOCK_EX);
469

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

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

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

    
496
		$voucherlck = lock('voucher', LOCK_EX);
497

    
498
            // create active and used DB per roll on ramdisk from config
499
            foreach ($config['voucher']['roll'] as $rollent) {
500

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

    
519
		unlock($voucherlck);
520
		if ($g['booting'])
521
		    echo "done\n";
522
        }
523

    
524
	return 0;
525
}
526

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

    
533
	$fd = fopen("{$g['vardb_path']}/voucher_used_$roll.db", "w");
534
	if ($fd) {
535
		fwrite($fd, $vdb . "\n");
536
		fclose($fd);
537
	} else
538
        	voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_used_%2$s.db'), $g['vardb_path'], $roll));
539
}
540

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

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

    
567
				/* Triger a sync of the vouchers on config */
568
				send_event("service sync vouchers");
569
			}
570
		}
571
	}
572
	return $active;
573
}
574

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

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

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

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

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

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

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

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

    
632
    define_syslog_variables();
633
    $message = trim($message);
634
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
635
    syslog($priority, sprintf(gettext("Voucher: %s"),$message));
636
    closelog();
637
}
638

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

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

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

    
671
    unlock($voucherlck);
672

    
673
    write_config("Synching vouchers");
674
    return;
675
}
676

    
677
?>
(51-51/61)