Project

General

Profile

Download (24.8 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
			if ($g['booting'])
466
			    echo gettext("Enabling voucher support... ");
467
			$cpzone = $voucherzone;
468
			$error = voucher_configure_zone($sync);
469
			if ($g['booting']) {
470
				if ($error)
471
					echo "error\n";
472
				else
473
					echo "done\n";
474
			}
475
		}
476
	}
477
}
478

    
479
function voucher_configure_zone($sync = false) {
480
	global $config, $g, $cpzone;
481

    
482
	if (!isset($config['voucher'][$cpzone]['enable']))
483
		return 0;
484

    
485
	if ($sync == true)
486
	    captiveportal_syslog("Writing voucher db from sync data...");
487

    
488
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
489

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

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

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

    
516
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
517

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

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

    
539
		unlock($voucherlck);
540
        }
541

    
542
	return 0;
543
}
544

    
545
/* write bitstring of used vouchers to ramdisk. 
546
 * Bitstring must already be base64_encoded!
547
 */
548
function voucher_write_used_db($roll, $vdb) {
549
	global $g, $cpzone;
550

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

    
559
/* return assoc array of active vouchers with activation timestamp
560
 * voucher is index. 
561
 */
562
function voucher_read_active_db($roll) {
563
	global $g, $cpzone;
564

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

    
585
				/* Triger a sync of the vouchers on config */
586
				send_event("service sync vouchers");
587
			}
588
		}
589
	}
590
	return $active;
591
}
592

    
593
/* store array of active vouchers back to DB */
594
function voucher_write_active_db($roll, $active) {
595
    global $g, $cpzone;
596

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

    
607
/* return how many vouchers are marked used on a roll */
608
function voucher_used_count($roll) {
609
    global $g, $cpzone;
610

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

    
624
function voucher_read_used_db($roll) {
625
    global $g, $cpzone;
626

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

    
641
function voucher_unlink_db($roll) {
642
    global $g, $cpzone;
643
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
644
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
645
}
646

    
647
/* we share the log with captiveportal for now */
648
function voucher_log($priority, $message) {
649

    
650
    define_syslog_variables();
651
    $message = trim($message);
652
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
653
    syslog($priority, sprintf(gettext("Voucher: %s"),$message));
654
    closelog();
655
}
656

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

    
663
	if (is_array($config['voucher'])) {
664
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
665
			$cpzone = $voucherzone;
666
			voucher_save_db_to_config_zone();
667
		}
668
	}
669
}
670

    
671
function voucher_save_db_to_config_zone() {
672
    global $config, $g, $cpzone;
673
    
674
    if (!isset($config['voucher'][$cpzone]['enable']))
675
        return;   // no vouchers or don't want to save DB's
676

    
677
    $voucherlck = lock("voucher{$cpzone}", LOCK_EX);
678

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

    
700
    unlock($voucherlck);
701

    
702
    write_config("Synching vouchers");
703
    return;
704
}
705

    
706
?>
(53-53/63)