Project

General

Profile

Download (24.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
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, $cpzone;
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
	\$cpzone = $cpzone;
54
	voucher_expire(\$vouchers);
55

    
56
EOF;
57

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

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

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

    
85
	return $toreturn;
86
}
87

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

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

    
105
EOF;
106

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

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

    
132
	$toreturn =  XML_RPC_Decode($resp->value());
133

    
134
	return $toreturn;
135
}
136

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

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

    
155
EOF;
156

    
157
	/* assemble xmlrpc payload */
158
	$params = array(
159
		XML_RPC_encode($password),
160
		XML_RPC_encode($execcmd)
161
	);
162

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

    
188
	return $toreturn['timeleft'];
189
}
190

    
191
function voucher_expire($voucher_received) {
192
	global $g, $config, $cpzone;
193

    
194
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
195

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

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

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

    
219
	$cpdb = captiveportal_read_db(false, 4); /* Indexed by Voucher */
220
	$unsetindexes[] = array();
221

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

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

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

    
267
	// Refresh active DBs
268
	if ($active_dirty == true) {
269
		foreach ($active_vouchers as $roll => $active)
270
			voucher_write_active_db($roll, $active);
271

    
272
		/* Triger a sync of the vouchers on config */
273
		send_event("service sync vouchers");
274
	}
275

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

    
288
	unlock($voucherlck);
289

    
290
	/* Write database */
291
	if (!empty($unsetindexes))
292
		captiveportal_write_db($cpdb, false, $unsetindexes);
293

    
294
	return true;
295
}
296

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

    
307
	if (!isset($config['voucher'][$cpzone]['enable']))
308
		return 0;
309

    
310
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
311

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

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

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

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

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

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

    
407
		return $test_result;
408
	}
409

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

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

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

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

    
449
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
450
	voucher_write_active_db($roll, $active_vouchers[$first_voucher_roll]);
451

    
452
	/* Triger a sync of the vouchers on config */
453
	send_event("service sync vouchers");
454

    
455
	unlock($voucherlck);
456

    
457
	return $total_minutes;
458
}
459

    
460
function voucher_configure($sync = false) {
461
	global $config, $g, $cpzone;
462

    
463
	if (is_array($config['voucher'])) {
464
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
465
			$cpzone = $voucherzone;
466
			voucher_configure_zone($sync);
467
		}
468
	}
469
}
470

    
471
function voucher_configure_zone($sync = false) {
472
	global $config, $g, $cpzone;
473

    
474
	if (!isset($config['voucher'][$cpzone]['enable']))
475
		return 0;
476

    
477
        if ($g['booting'])
478
            echo "Enabling voucher support... ";
479
	if ($sync == true)
480
	    captiveportal_syslog("Writing voucher db from sync data...");
481

    
482
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
483

    
484
        /* write public key used to verify vouchers */
485
        $pubkey = base64_decode($config['voucher'][$cpzone]['publickey']);
486
        $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w");
487
        if (!$fd) {
488
            captiveportal_syslog("Voucher error: cannot write voucher.public\n");
489
	    unlock($voucherlck);
490
            return 1;
491
        }
492
        fwrite($fd, $pubkey);
493
        fclose($fd);
494
        @chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600);
495

    
496
        /* write config file used by voucher binary to decode vouchers */
497
        $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w");
498
        if (!$fd) {
499
	    printf(gettext("Error: cannot write voucher.cfg") . "\n");
500
	    unlock($voucherlck);
501
            return 1;
502
        }
503
        fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n");
504
        fclose($fd);
505
        @chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600);
506
	unlock($voucherlck);
507

    
508
        if (($g['booting'] || $sync == true) && is_array($config['voucher'][$cpzone]['roll'])) {
509

    
510
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
511

    
512
            // create active and used DB per roll on ramdisk from config
513
            foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
514

    
515
                $roll = $rollent['number'];
516
                voucher_write_used_db($roll, $rollent['used']);
517
                $minutes = $rollent['minutes'];
518
                $active_vouchers = array();
519
                $a_active = &$rollent['active'];
520
                if (is_array($a_active)) {
521
                    foreach ($a_active as $activent) {
522
                        $voucher = $activent['voucher'];
523
                        $timestamp = $activent['timestamp'];
524
                        $minutes = $activent['minutes'];
525
                        // its tempting to check for expired timestamps, but during
526
                        // bootup, we most likely don't have the correct time time.
527
                        $active_vouchers[$voucher] = "$timestamp,$minutes";
528
                    }
529
                }
530
                voucher_write_active_db($roll, $active_vouchers);
531
            }
532

    
533
		unlock($voucherlck);
534
		if ($g['booting'])
535
		    echo "done\n";
536
        }
537

    
538
	return 0;
539
}
540

    
541
/* write bitstring of used vouchers to ramdisk. 
542
 * Bitstring must already be base64_encoded!
543
 */
544
function voucher_write_used_db($roll, $vdb) {
545
	global $g, $cpzone;
546

    
547
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w");
548
	if ($fd) {
549
		fwrite($fd, $vdb . "\n");
550
		fclose($fd);
551
	} else
552
		voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
553
}
554

    
555
/* return assoc array of active vouchers with activation timestamp
556
 * voucher is index. 
557
 */
558
function voucher_read_active_db($roll) {
559
	global $g, $cpzone;
560

    
561
	$active = array();
562
	$dirty = 0;
563
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db";
564
	if (file_exists($file)) {
565
		$fd = fopen($file, "r");
566
		if ($fd) {
567
			while (!feof($fd)) {
568
				$line = trim(fgets($fd));
569
				if ($line) {
570
					list($voucher,$timestamp,$minutes) = explode(",", $line); // voucher,timestamp
571
					if ((($timestamp + (60*$minutes)) - time()) > 0)
572
						$active[$voucher] = "$timestamp,$minutes";
573
					else
574
						$dirty=1;
575
				}
576
			}
577
			fclose($fd);
578
			if ($dirty) { // if we found expired entries, lets save our snapshot
579
				voucher_write_active_db($roll, $active);
580

    
581
				/* Triger a sync of the vouchers on config */
582
				send_event("service sync vouchers");
583
			}
584
		}
585
	}
586
	return $active;
587
}
588

    
589
/* store array of active vouchers back to DB */
590
function voucher_write_active_db($roll, $active) {
591
    global $g, $cpzone;
592

    
593
	if (!is_array($active))
594
		return;
595
    $fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w");
596
    if ($fd) {
597
        foreach($active as $voucher => $value)
598
            fwrite($fd, "$voucher,$value\n");
599
        fclose($fd);
600
    }
601
}
602

    
603
/* return how many vouchers are marked used on a roll */
604
function voucher_used_count($roll) {
605
    global $g, $cpzone;
606

    
607
    $bitstring = voucher_read_used_db($roll);
608
    $max = strlen($bitstring) * 8;
609
    $used = 0;
610
    for ($i = 1; $i <= $max; $i++) {
611
        // check if ticket already used or not. 
612
        $pos = $i >> 3;            // divide by 8 -> octet
613
        $mask = 1 << ($i % 8);  // mask to test bit in octet
614
        if (ord($bitstring[$pos]) & $mask)
615
            $used++;
616
    }   
617
    return $used;
618
}
619

    
620
function voucher_read_used_db($roll) {
621
    global $g, $cpzone;
622

    
623
    $vdb = "";
624
    $file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db";
625
    if (file_exists($file)) {
626
        $fd = fopen($file, "r");
627
        if ($fd) {
628
            $vdb = trim(fgets($fd));
629
            fclose($fd);
630
        } else {
631
	    voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
632
        }
633
    }
634
    return base64_decode($vdb);
635
}
636

    
637
function voucher_unlink_db($roll) {
638
    global $g, $cpzone;
639
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
640
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
641
}
642

    
643
/* we share the log with captiveportal for now */
644
function voucher_log($priority, $message) {
645

    
646
    define_syslog_variables();
647
    $message = trim($message);
648
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
649
    syslog($priority, sprintf(gettext("Voucher: %s"),$message));
650
    closelog();
651
}
652

    
653
/* Save active and used voucher DB into XML config and write it to flash
654
 * Called during reboot -> system_reboot_cleanup() and every active voucher change
655
 */
656
function voucher_save_db_to_config() {
657
    global $config, $g, $cpzone;
658

    
659
	if (is_array($config['voucher'])) {
660
		foreach ($configure['voucher'] as $voucherzone => $vcfg) {
661
			$cpzone = $voucherzone;
662
			voucher_save_db_to_config_zone();
663
		}
664
	}
665
}
666

    
667
function voucher_save_db_to_confifg_zone() {
668
    global $config, $g, $cpzone;
669
    
670
    if (!isset($config['voucher'][$cpzone]['enable']))
671
        return;   // no vouchers or don't want to save DB's
672

    
673
    $voucherlck = lock("voucher{$cpzone}", LOCK_EX);
674

    
675
    // walk all active rolls and save runtime DB's to flash
676
    $a_roll = &$config['voucher'][$cpzone]['roll'];
677
    while (list($key, $value) = each($a_roll)) {
678
        $rollent = &$a_roll[$key];
679
        $roll = $rollent['number'];
680
        $bitmask = voucher_read_used_db($roll);
681
        $rollent['used'] = base64_encode($bitmask);
682
        $active_vouchers = voucher_read_active_db($roll);
683
        $db = array();
684
		$dbi = 1;
685
        foreach($active_vouchers as $voucher => $line) {
686
            list($timestamp,$minutes) = explode(",", $line);
687
            $activent['voucher'] = $voucher;
688
            $activent['timestamp'] = $timestamp;
689
            $activent['minutes'] = $minutes;
690
            $db["v{$dbi}"] = $activent;
691
	    $dbi++;
692
        }
693
        $rollent['active'] = $db;
694
    }
695

    
696
    unlock($voucherlck);
697

    
698
    write_config("Synching vouchers");
699
    return;
700
}
701

    
702
?>
(51-51/61)