Projet

Général

Profil

Télécharger (26,1 ko) Statistiques
| Branche: | Tag: | Révision:

univnautes / etc / inc / voucher.inc @ 7a1f391a

1
<?php
2
/*
3
	Copyright (C) 2010-2012 Ermal Luci <eri@pfsense.org>
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

    
44
	$protocol = "http";
45
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
46
	    $config['system']['webgui']['protocol'] == "https")
47
		$protocol = "https";
48
	if ($protocol == "https" || $port == "443")
49
		$url = "https://{$syncip}";
50
	else
51
		$url = "http://{$syncip}";
52

    
53
	/* Construct code that is run on remote machine */
54
	$method = 'pfsense.exec_php';
55
	$execcmd  = <<<EOF
56
	global \$cpzone;
57
	require_once('/etc/inc/captiveportal.inc');
58
	require_once('/etc/inc/voucher.inc');
59
	\$cpzone = "$cpzone";
60
	voucher_expire("$vouchers");
61

    
62
EOF;
63

    
64
	/* assemble xmlrpc payload */
65
	$params = array(
66
		XML_RPC_encode($password),
67
		XML_RPC_encode($execcmd)
68
	);
69

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

    
89
	$toreturn =  XML_RPC_Decode($resp->value());
90

    
91
	return $toreturn;
92
}
93

    
94
function xmlrpc_sync_voucher_disconnect($dbent, $syncip, $port, $password, $username, $term_cause = 1, $stop_time = null) {
95
	global $g, $config, $cpzone;
96
	require_once("xmlrpc.inc");
97

    
98
	$protocol = "http";
99
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
100
	    $config['system']['webgui']['protocol'] == "https")
101
		$protocol = "https";
102
	if ($protocol == "https" || $port == "443")
103
		$url = "https://{$syncip}";
104
	else
105
		$url = "http://{$syncip}";
106

    
107
	/* Construct code that is run on remote machine */
108
	$dbent_str = serialize($dbent);
109
	$tmp_stop_time = (isset($stop_time)) ? $stop_time : "null";
110
	$method = 'pfsense.exec_php';
111
	$execcmd  = <<<EOF
112
	global \$cpzone;
113
	require_once('/etc/inc/captiveportal.inc');
114
	require_once('/etc/inc/voucher.inc');
115
	\$cpzone = "$cpzone";
116
	\$radiusservers = captiveportal_get_radius_servers();
117
	\$dbent = unserialize("$dbent_str");
118
	captiveportal_disconnect(\$dbent, \$radiusservers, $term_cause, $tmp_stop_time);
119

    
120
EOF;
121

    
122
	/* assemble xmlrpc payload */
123
	$params = array(
124
		XML_RPC_encode($password),
125
		XML_RPC_encode($execcmd)
126
	);
127

    
128
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
129
	$msg = new XML_RPC_Message($method, $params);
130
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
131
	$cli->setCredentials($username, $password);
132
	$resp = $cli->send($msg, "250");
133
	if(!is_object($resp)) {
134
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
135
		log_error($error);
136
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
137
		return false;
138
	} elseif($resp->faultCode()) {
139
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
140
		log_error($error);
141
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
142
		return false;
143
	} else {
144
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
145
	}
146

    
147
	$toreturn =  XML_RPC_Decode($resp->value());
148

    
149
	return $toreturn;
150
}
151

    
152
function xmlrpc_sync_used_voucher($voucher_received, $syncip, $port, $password, $username) {
153
	global $g, $config, $cpzone;
154
	require_once("xmlrpc.inc");
155

    
156
	$protocol = "http";
157
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
158
	    $config['system']['webgui']['protocol'] == "https")
159
		$protocol = "https";
160
	if ($protocol == "https" || $port == "443")
161
		$url = "https://{$syncip}";
162
	else
163
		$url = "http://{$syncip}";
164

    
165
	/* Construct code that is run on remote machine */
166
	$method = 'pfsense.exec_php';
167
	$execcmd  = <<<EOF
168
	global \$cpzone;
169
	require_once('/etc/inc/voucher.inc');
170
	\$cpzone = "$cpzone";
171
	\$timeleft = voucher_auth("$voucher_received");
172
	\$toreturn = array();
173
	\$toreturn['timeleft'] = \$timeleft;
174
	\$toreturn['voucher'] = array();
175
	\$toreturn['voucher']['roll'] = \$config['voucher'][\$cpzone]['roll'];
176

    
177
EOF;
178

    
179
	/* assemble xmlrpc payload */
180
	$params = array(
181
		XML_RPC_encode($password),
182
		XML_RPC_encode($execcmd)
183
	);
184

    
185
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
186
	$msg = new XML_RPC_Message($method, $params);
187
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
188
	$cli->setCredentials($username, $password);
189
	$resp = $cli->send($msg, "250");
190
	if(!is_object($resp)) {
191
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
192
		log_error($error);
193
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
194
		return null; // $timeleft
195
	} elseif($resp->faultCode()) {
196
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
197
		log_error($error);
198
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
199
		return null; // $timeleft
200
	} else {
201
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
202
	}
203
	$toreturn =  XML_RPC_Decode($resp->value());
204
	if (!is_array($config['voucher']))
205
		$config['voucher'] = array();
206

    
207
	if (is_array($toreturn['voucher']) && is_array($toreturn['voucher']['roll'])) {
208
		$config['voucher'][$cpzone]['roll'] = $toreturn['voucher']['roll'];
209
		write_config("Captive Portal Voucher database synchronized with {$url}");
210
		voucher_configure_zone(true);
211
		unset($toreturn['voucher']);
212
	} else if (!isset($toreturn['timeleft']))
213
		return null;
214

    
215
	return $toreturn['timeleft'];
216
}
217

    
218
function voucher_expire($voucher_received) {
219
	global $g, $config, $cpzone;
220

    
221
	// XMLRPC Call over to the master Voucher node
222
	if(!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
223
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
224
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
225
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
226
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
227
		xmlrpc_sync_voucher_expire($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
228
	}
229

    
230
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
231

    
232
	// read rolls into assoc array with rollid as key and minutes as value
233
	$tickets_per_roll = array();
234
	$minutes_per_roll = array();
235
	if (is_array($config['voucher'][$cpzone]['roll'])) {
236
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
237
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
238
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
239
		}
240
	}
241

    
242
	// split into an array. Useful for multiple vouchers given
243
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received); 
244
	$active_dirty = false;
245
	$unsetindexes = array();
246

    
247
	// go through all received vouchers, check their valid and extract
248
	// Roll# and Ticket# using the external readvoucher binary
249
	foreach ($a_vouchers_received as $voucher) {
250
		$v = escapeshellarg($voucher);
251
		if (strlen($voucher) < 3)
252
			continue;   // seems too short to be a voucher!
253

    
254
		unset($output);
255
		$_gb = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v", $output);
256
		list($status, $roll, $nr) = explode(" ", $output[0]);
257
		if ($status == "OK") {
258
			// check if we have this ticket on a registered roll for this ticket 
259
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
260
				// voucher is from a registered roll. 
261
				if (!isset($active_vouchers[$roll]))
262
					$active_vouchers[$roll] = voucher_read_active_db($roll);
263
				// valid voucher. Store roll# and ticket#
264
				if (!empty($active_vouchers[$roll][$voucher])) {
265
					$active_dirty = true;
266
					unset($active_vouchers[$roll][$voucher]);
267
				}
268
				// check if voucher already marked as used
269
				if (!isset($bitstring[$roll]))
270
					$bitstring[$roll] = voucher_read_used_db($roll);
271
				$pos = $nr >> 3; // divide by 8 -> octet
272
				$mask = 1 << ($nr % 8);
273
				// mark bit for this voucher as used
274
				if (!(ord($bitstring[$roll][$pos]) & $mask))
275
					$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
276
				captiveportal_syslog("{$voucher} ({$roll}/{$nr}) forced to expire");
277

    
278
				/* Check if this voucher has any active sessions */
279
				$cpentry = captiveportal_read_db("WHERE username = '{$voucher}'");
280
				if (!empty($cpentry)) {
281
					captiveportal_disconnect($cpentry,null,13);
282
					captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"FORCLY TERMINATING VOUCHER {$voucher} SESSION");
283
					$unsetindexes[] = $cpentry[5];
284
				}
285
			} else
286
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registererd Roll");
287
		} else
288
			// hmm, thats weird ... not what I expected
289
			captiveportal_syslog("$voucher invalid: {$output[0]}!!");
290
	}
291

    
292
	// Refresh active DBs
293
	if ($active_dirty == true) {
294
		foreach ($active_vouchers as $roll => $active)
295
			voucher_write_active_db($roll, $active);
296
		unset($active_vouchers);
297

    
298
		/* Triger a sync of the vouchers on config */
299
		send_event("service sync vouchers");
300
	}
301

    
302
	// Write back the used DB's
303
	if (is_array($bitstring)) {
304
		foreach ($bitstring as $roll => $used) {
305
			if(is_array($used)) {
306
				foreach($used as $u)
307
					voucher_write_used_db($roll, base64_encode($u));
308
			} else {
309
				voucher_write_used_db($roll, base64_encode($used));
310
			}
311
		}
312
		unset($bitstring);
313
	}
314

    
315
	unlock($voucherlck);
316

    
317
	/* Write database */
318
	if (!empty($unsetindexes))
319
		captiveportal_remove_entries($unsetindexes);
320

    
321
	return true;
322
}
323

    
324
/* 
325
 * Authenticate a voucher and return the remaining time credit in minutes
326
 * if $test is set, don't mark the voucher as used nor add it to the list
327
 * of active vouchers
328
 * If $test is set, simply test the voucher. Don't change anything
329
 * but return a more verbose error and result message back
330
 */
331
function voucher_auth($voucher_received, $test = 0) {
332
	global $g, $config, $cpzone, $dbc;
333

    
334
	if (!isset($config['voucher'][$cpzone]['enable']))
335
		return 0;
336

    
337
	// XMLRPC Call over to the master Voucher node
338
	if(!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
339
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
340
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
341
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
342
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
343
		$remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
344
	}
345

    
346
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
347

    
348
	// read rolls into assoc array with rollid as key and minutes as value
349
	$tickets_per_roll = array();
350
	$minutes_per_roll = array();
351
	if (is_array($config['voucher'][$cpzone]['roll'])) {
352
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
353
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
354
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
355
		}
356
	}
357

    
358
	// split into an array. Useful for multiple vouchers given
359
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received); 
360
	$error = 0;
361
	$test_result = array();     // used to display for voucher test option in GUI
362
	$total_minutes = 0;
363
	$first_voucher = "";
364
	$first_voucher_roll = 0;
365

    
366
	// go through all received vouchers, check their valid and extract
367
	// Roll# and Ticket# using the external readvoucher binary
368
	foreach ($a_vouchers_received as $voucher) {
369
		$v = escapeshellarg($voucher);
370
		if (strlen($voucher) < 3)
371
			continue;   // seems too short to be a voucher!
372

    
373
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v");
374
		list($status, $roll, $nr) = explode(" ", $result);
375
		if ($status == "OK") {
376
			if (!$first_voucher) {
377
				// store first voucher. Thats the one we give the timecredit
378
				$first_voucher = $voucher;
379
				$first_voucher_roll = $roll;
380
			}
381
			// check if we have this ticket on a registered roll for this ticket 
382
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
383
				// voucher is from a registered roll. 
384
				if (!isset($active_vouchers[$roll]))
385
					$active_vouchers[$roll] = voucher_read_active_db($roll);
386
				// valid voucher. Store roll# and ticket#
387
				if (!empty($active_vouchers[$roll][$voucher])) {
388
					list($timestamp,$minutes) = explode(",", $active_vouchers[$roll][$voucher]);
389
					// we have an already active voucher here.
390
					$remaining = intval((($timestamp + (60*$minutes)) - time())/60);
391
					$test_result[] = sprintf(gettext('%1$s (%2$s/%3$s) active and good for %4$d Minutes'), $voucher, $roll, $nr, $remaining);
392
					$total_minutes += $remaining;
393
				} else {
394
					// voucher not used. Check if ticket Id is on the roll (not too high)
395
					// and if the ticket is marked used.
396
					// check if voucher already marked as used
397
					if (!isset($bitstring[$roll]))
398
						$bitstring[$roll] = voucher_read_used_db($roll);
399
					$pos = $nr >> 3; // divide by 8 -> octet
400
					$mask = 1 << ($nr % 8);
401
					if (ord($bitstring[$roll][$pos]) & $mask) {
402
						$test_result[] = "$voucher ($roll/$nr) already used and expired";
403
						captiveportal_syslog("$voucher ($roll/$nr) already used and expired");
404
						$total_minutes = -1;    // voucher expired
405
						$error++;
406
					} else {
407
						// mark bit for this voucher as used
408
						$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
409
						$test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
410
						$total_minutes += $minutes_per_roll[$roll];
411
					}
412
				}
413
			} else {
414
				$test_result[] = "$voucher ($roll/$nr): not found on any registererd Roll";
415
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registererd Roll");
416
			}
417
		} else {
418
			// hmm, thats weird ... not what I expected
419
			$test_result[] = "$voucher invalid: $result !!";
420
			captiveportal_syslog("$voucher invalid: $result !!");
421
			$error++;
422
		}
423
	}
424

    
425
	// if this was a test call, we're done. Return the result.
426
	if ($test) {
427
		if ($error) {
428
			$test_result[] = gettext("Access denied!");
429
		} else {
430
			$test_result[] = sprintf(gettext("Access granted for %d Minutes in total."),$total_minutes);
431
		}
432
		unlock($voucherlck);
433

    
434
		return $test_result;
435
	}
436

    
437
	// if we had an error (one of the vouchers is invalid), return 0.
438
	// Discussion: we could return the time remaining for good vouchers, but then
439
	// the user wouldn't know that he used at least one invalid voucher.
440
	if ($error) {
441
		unlock($voucherlck);
442
		if ($total_minutes > 0)     // probably not needed, but want to make sure
443
			$total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
444
		return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
445
	}
446

    
447
	// If we did a XMLRPC sync earlier check the timeleft
448
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
449
		if (!is_null($remote_time_used))
450
			$total_minutes = $remote_time_used;
451
		else if ($remote_time_used < $total_minutes) 
452
			$total_minutes -= $remote_time_used;
453
	}
454

    
455
	// All given vouchers were valid and this isn't simply a test.
456
	// Write back the used DB's
457
	if (is_array($bitstring)) {
458
		foreach ($bitstring as $roll => $used) {
459
			if(is_array($used)) {
460
				foreach($used as $u)
461
					voucher_write_used_db($roll, base64_encode($u));
462
			} else {
463
				voucher_write_used_db($roll, base64_encode($used));
464
			}
465
		}
466
	}
467

    
468
	// Active DB: we only add the first voucher if multiple given
469
	// and give that one all the time credit. This allows the user to logout and
470
	// log in later using just the first voucher. It also keeps username limited
471
	// to one voucher and that voucher shows the correct time credit in 'active vouchers'
472
	if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) {
473
		list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]);
474
	} else {
475
		$timestamp = time();    // new voucher
476
		$minutes = $total_minutes;
477
	}
478

    
479
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
480
	voucher_write_active_db($first_voucher_roll, $active_vouchers[$first_voucher_roll]);
481

    
482
	/* Triger a sync of the vouchers on config */
483
	send_event("service sync vouchers");
484

    
485
	unlock($voucherlck);
486

    
487
	return $total_minutes;
488
}
489

    
490
function voucher_configure($sync = false) {
491
	global $config, $g, $cpzone;
492

    
493
	if (is_array($config['voucher'])) {
494
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
495
			if ($g['booting'])
496
			    echo gettext("Enabling voucher support... ");
497
			$cpzone = $voucherzone;
498
			$error = voucher_configure_zone($sync);
499
			if ($g['booting']) {
500
				if ($error)
501
					echo "error\n";
502
				else
503
					echo "done\n";
504
			}
505
		}
506
	}
507
}
508

    
509
function voucher_configure_zone($sync = false) {
510
	global $config, $g, $cpzone;
511

    
512
	if (!isset($config['voucher'][$cpzone]['enable']))
513
		return 0;
514

    
515
	if ($sync == true)
516
	    captiveportal_syslog("Writing voucher db from sync data...");
517

    
518
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
519

    
520
        /* write public key used to verify vouchers */
521
        $pubkey = base64_decode($config['voucher'][$cpzone]['publickey']);
522
        $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w");
523
        if (!$fd) {
524
            captiveportal_syslog("Voucher error: cannot write voucher.public\n");
525
	    unlock($voucherlck);
526
            return 1;
527
        }
528
        fwrite($fd, $pubkey);
529
        fclose($fd);
530
        @chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600);
531

    
532
        /* write config file used by voucher binary to decode vouchers */
533
        $fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w");
534
        if (!$fd) {
535
	    printf(gettext("Error: cannot write voucher.cfg") . "\n");
536
	    unlock($voucherlck);
537
            return 1;
538
        }
539
        fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n");
540
        fclose($fd);
541
        @chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600);
542
	unlock($voucherlck);
543

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

    
546
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
547

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

    
551
                $roll = $rollent['number'];
552
                voucher_write_used_db($roll, $rollent['used']);
553
                $minutes = $rollent['minutes'];
554
                $active_vouchers = array();
555
                $a_active = &$rollent['active'];
556
                if (is_array($a_active)) {
557
                    foreach ($a_active as $activent) {
558
                        $voucher = $activent['voucher'];
559
                        $timestamp = $activent['timestamp'];
560
                        $minutes = $activent['minutes'];
561
                        // its tempting to check for expired timestamps, but during
562
                        // bootup, we most likely don't have the correct time time.
563
                        $active_vouchers[$voucher] = "$timestamp,$minutes";
564
                    }
565
                }
566
                voucher_write_active_db($roll, $active_vouchers);
567
            }
568

    
569
		unlock($voucherlck);
570
        }
571

    
572
	return 0;
573
}
574

    
575
/* write bitstring of used vouchers to ramdisk. 
576
 * Bitstring must already be base64_encoded!
577
 */
578
function voucher_write_used_db($roll, $vdb) {
579
	global $g, $cpzone;
580

    
581
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w");
582
	if ($fd) {
583
		fwrite($fd, $vdb . "\n");
584
		fclose($fd);
585
	} else
586
		voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
587
}
588

    
589
/* return assoc array of active vouchers with activation timestamp
590
 * voucher is index. 
591
 */
592
function voucher_read_active_db($roll) {
593
	global $g, $cpzone;
594

    
595
	$active = array();
596
	$dirty = 0;
597
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db";
598
	if (file_exists($file)) {
599
		$fd = fopen($file, "r");
600
		if ($fd) {
601
			while (!feof($fd)) {
602
				$line = trim(fgets($fd));
603
				if ($line) {
604
					list($voucher,$timestamp,$minutes) = explode(",", $line); // voucher,timestamp
605
					if ((($timestamp + (60*$minutes)) - time()) > 0)
606
						$active[$voucher] = "$timestamp,$minutes";
607
					else
608
						$dirty=1;
609
				}
610
			}
611
			fclose($fd);
612
			if ($dirty) { // if we found expired entries, lets save our snapshot
613
				voucher_write_active_db($roll, $active);
614

    
615
				/* Triger a sync of the vouchers on config */
616
				send_event("service sync vouchers");
617
			}
618
		}
619
	}
620
	return $active;
621
}
622

    
623
/* store array of active vouchers back to DB */
624
function voucher_write_active_db($roll, $active) {
625
    global $g, $cpzone;
626

    
627
	if (!is_array($active))
628
		return;
629
    $fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w");
630
    if ($fd) {
631
        foreach($active as $voucher => $value)
632
            fwrite($fd, "$voucher,$value\n");
633
        fclose($fd);
634
    }
635
}
636

    
637
/* return how many vouchers are marked used on a roll */
638
function voucher_used_count($roll) {
639
    global $g, $cpzone;
640

    
641
    $bitstring = voucher_read_used_db($roll);
642
    $max = strlen($bitstring) * 8;
643
    $used = 0;
644
    for ($i = 1; $i <= $max; $i++) {
645
        // check if ticket already used or not. 
646
        $pos = $i >> 3;            // divide by 8 -> octet
647
        $mask = 1 << ($i % 8);  // mask to test bit in octet
648
        if (ord($bitstring[$pos]) & $mask)
649
            $used++;
650
    }
651
    unset($bitstring);
652

    
653
    return $used;
654
}
655

    
656
function voucher_read_used_db($roll) {
657
    global $g, $cpzone;
658

    
659
    $vdb = "";
660
    $file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db";
661
    if (file_exists($file)) {
662
        $fd = fopen($file, "r");
663
        if ($fd) {
664
            $vdb = trim(fgets($fd));
665
            fclose($fd);
666
        } else {
667
	    voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
668
        }
669
    }
670
    return base64_decode($vdb);
671
}
672

    
673
function voucher_unlink_db($roll) {
674
    global $g, $cpzone;
675
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
676
    @unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
677
}
678

    
679
/* we share the log with captiveportal for now */
680
function voucher_log($priority, $message) {
681

    
682
    $message = trim($message);
683
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
684
    syslog($priority, sprintf(gettext("Voucher: %s"),$message));
685
    closelog();
686
}
687

    
688
/* Save active and used voucher DB into XML config and write it to flash
689
 * Called during reboot -> system_reboot_cleanup() and every active voucher change
690
 */
691
function voucher_save_db_to_config() {
692
    global $config, $g, $cpzone;
693

    
694
	if (is_array($config['voucher'])) {
695
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
696
			$cpzone = $voucherzone;
697
			voucher_save_db_to_config_zone();
698
		}
699
	}
700
}
701

    
702
function voucher_save_db_to_config_zone() {
703
    global $config, $g, $cpzone;
704
    
705
    if (!isset($config['voucher'][$cpzone]['enable']))
706
        return;   // no vouchers or don't want to save DB's
707

    
708
    if (!is_array($config['voucher'][$cpzone]['roll']))
709
	return;
710

    
711
    $voucherlck = lock("voucher{$cpzone}", LOCK_EX);
712

    
713
    // walk all active rolls and save runtime DB's to flash
714
    $a_roll = &$config['voucher'][$cpzone]['roll'];
715
    while (list($key, $value) = each($a_roll)) {
716
        $rollent = &$a_roll[$key];
717
        $roll = $rollent['number'];
718
        $bitmask = voucher_read_used_db($roll);
719
        $rollent['used'] = base64_encode($bitmask);
720
        $active_vouchers = voucher_read_active_db($roll);
721
        $db = array();
722
		$dbi = 1;
723
        foreach($active_vouchers as $voucher => $line) {
724
            list($timestamp,$minutes) = explode(",", $line);
725
            $activent['voucher'] = $voucher;
726
            $activent['timestamp'] = $timestamp;
727
            $activent['minutes'] = $minutes;
728
            $db["v{$dbi}"] = $activent;
729
	    $dbi++;
730
        }
731
        $rollent['active'] = $db;
732
	unset($active_vouchers);
733
    }
734

    
735
    unlock($voucherlck);
736

    
737
    write_config("Synching vouchers");
738
    return;
739
}
740

    
741
?>
(57-57/67)