Project

General

Profile

Download (26 KB) Statistics
| Branch: | Tag: | Revision:
1 336e3c1c Charlie
<?php
2
/*
3 ce77a9c4 Phil Davis
	voucher.inc
4 d98a2e6a Jose Luis Duran
	Copyright (C) 2010-2012 Ermal Luçi <eri@pfsense.org>
5 4d5bbdfb Scott Ullrich
	Copyright (C) 2010 Scott Ullrich <sullrich@gmail.com>
6 79262830 Phil Davis
	Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>
7
	All rights reserved.
8
9
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11
12
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14
15
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18
19
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29 336e3c1c Charlie
30
*/
31 523855b0 Scott Ullrich
32
/*
33 5ebe85e9 Ermal
	pfSense_BUILDER_BINARIES:	/usr/local/bin/voucher
34 523855b0 Scott Ullrich
	pfSense_MODULE:	captiveportal
35
*/
36
37 336e3c1c Charlie
/* include all configuration functions */
38 79262830 Phil Davis
if (!function_exists('captiveportal_syslog')) {
39 4e8d55dd Scott Ullrich
	require_once("captiveportal.inc");
40 79262830 Phil Davis
}
41 336e3c1c Charlie
42 05771a24 Ermal
function xmlrpc_sync_voucher_expire($vouchers, $syncip, $port, $password, $username) {
43 b4792bf8 Ermal
	global $g, $config, $cpzone;
44 05771a24 Ermal
	require_once("xmlrpc.inc");
45 f9d480ff Ermal
46
	$protocol = "http";
47
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
48 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
49 f9d480ff Ermal
		$protocol = "https";
50 79262830 Phil Davis
	}
51
	if ($protocol == "https" || $port == "443") {
52 05771a24 Ermal
		$url = "https://{$syncip}";
53 79262830 Phil Davis
	} else {
54 d6e569ae Ermal
		$url = "http://{$syncip}";
55 79262830 Phil Davis
	}
56 05771a24 Ermal
57
	/* Construct code that is run on remote machine */
58
	$method = 'pfsense.exec_php';
59 6c07db48 Phil Davis
	$execcmd = <<<EOF
60 fac36dfd Ermal
	global \$cpzone;
61 05771a24 Ermal
	require_once('/etc/inc/captiveportal.inc');
62
	require_once('/etc/inc/voucher.inc');
63 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
64
	voucher_expire("$vouchers");
65 05771a24 Ermal
66
EOF;
67
68
	/* assemble xmlrpc payload */
69
	$params = array(
70
		XML_RPC_encode($password),
71
		XML_RPC_encode($execcmd)
72
	);
73
74
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
75
	$msg = new XML_RPC_Message($method, $params);
76
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
77
	$cli->setCredentials($username, $password);
78
	$resp = $cli->send($msg, "250");
79 79262830 Phil Davis
	if (!is_object($resp)) {
80 05771a24 Ermal
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
81
		log_error($error);
82
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
83
		return false;
84 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
85 05771a24 Ermal
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
86
		log_error($error);
87
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
88
		return false;
89
	} else {
90
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
91
	}
92
93 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
94 05771a24 Ermal
95
	return $toreturn;
96
}
97
98 a18eeb51 bcyrill
function xmlrpc_sync_voucher_disconnect($dbent, $syncip, $port, $password, $username, $term_cause = 1, $stop_time = null) {
99 b4792bf8 Ermal
	global $g, $config, $cpzone;
100 d322e3b3 Scott Ullrich
	require_once("xmlrpc.inc");
101 f9d480ff Ermal
102
	$protocol = "http";
103
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
104 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
105 f9d480ff Ermal
		$protocol = "https";
106 79262830 Phil Davis
	}
107
	if ($protocol == "https" || $port == "443") {
108 509e1202 Ermal
		$url = "https://{$syncip}";
109 79262830 Phil Davis
	} else {
110 d6e569ae Ermal
		$url = "http://{$syncip}";
111 79262830 Phil Davis
	}
112 d322e3b3 Scott Ullrich
113
	/* Construct code that is run on remote machine */
114 c2a6cda0 bcyrill
	$dbent_str = serialize($dbent);
115 bbe29bb1 bcyrill
	$tmp_stop_time = (isset($stop_time)) ? $stop_time : "null";
116 d322e3b3 Scott Ullrich
	$method = 'pfsense.exec_php';
117 6c07db48 Phil Davis
	$execcmd = <<<EOF
118 fac36dfd Ermal
	global \$cpzone;
119 d322e3b3 Scott Ullrich
	require_once('/etc/inc/captiveportal.inc');
120
	require_once('/etc/inc/voucher.inc');
121 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
122 d322e3b3 Scott Ullrich
	\$radiusservers = captiveportal_get_radius_servers();
123 a18eeb51 bcyrill
	\$dbent = unserialize("$dbent_str");
124 bbe29bb1 bcyrill
	captiveportal_disconnect(\$dbent, \$radiusservers, $term_cause, $tmp_stop_time);
125 d322e3b3 Scott Ullrich
126
EOF;
127
128
	/* assemble xmlrpc payload */
129
	$params = array(
130
		XML_RPC_encode($password),
131
		XML_RPC_encode($execcmd)
132
	);
133
134
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
135
	$msg = new XML_RPC_Message($method, $params);
136
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
137
	$cli->setCredentials($username, $password);
138
	$resp = $cli->send($msg, "250");
139 79262830 Phil Davis
	if (!is_object($resp)) {
140 d322e3b3 Scott Ullrich
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
141
		log_error($error);
142
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
143
		return false;
144 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
145 d322e3b3 Scott Ullrich
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
146
		log_error($error);
147
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
148
		return false;
149
	} else {
150
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
151
	}
152
153 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
154 d322e3b3 Scott Ullrich
155
	return $toreturn;
156
}
157
158 6c9ca678 Scott Ullrich
function xmlrpc_sync_used_voucher($voucher_received, $syncip, $port, $password, $username) {
159 b4792bf8 Ermal
	global $g, $config, $cpzone;
160 6c9ca678 Scott Ullrich
	require_once("xmlrpc.inc");
161 f9d480ff Ermal
162
	$protocol = "http";
163
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
164 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
165 f9d480ff Ermal
		$protocol = "https";
166 79262830 Phil Davis
	}
167
	if ($protocol == "https" || $port == "443") {
168 2fe347eb Ermal
		$url = "https://{$syncip}";
169 79262830 Phil Davis
	} else {
170 d6e569ae Ermal
		$url = "http://{$syncip}";
171 79262830 Phil Davis
	}
172 6c9ca678 Scott Ullrich
173
	/* Construct code that is run on remote machine */
174
	$method = 'pfsense.exec_php';
175 6c07db48 Phil Davis
	$execcmd = <<<EOF
176 fac36dfd Ermal
	global \$cpzone;
177 6c9ca678 Scott Ullrich
	require_once('/etc/inc/voucher.inc');
178 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
179
	\$timeleft = voucher_auth("$voucher_received");
180 6c9ca678 Scott Ullrich
	\$toreturn = array();
181
	\$toreturn['timeleft'] = \$timeleft;
182 90df43c6 Ermal
	\$toreturn['voucher'] = array();
183 bbe29bb1 bcyrill
	\$toreturn['voucher']['roll'] = \$config['voucher'][\$cpzone]['roll'];
184 6c9ca678 Scott Ullrich
185 fa3ab36a Scott Ullrich
EOF;
186 6c9ca678 Scott Ullrich
187
	/* assemble xmlrpc payload */
188
	$params = array(
189
		XML_RPC_encode($password),
190
		XML_RPC_encode($execcmd)
191
	);
192
193
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
194
	$msg = new XML_RPC_Message($method, $params);
195
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
196 2f384448 Scott Ullrich
	$cli->setCredentials($username, $password);
197 6c9ca678 Scott Ullrich
	$resp = $cli->send($msg, "250");
198 79262830 Phil Davis
	if (!is_object($resp)) {
199 6c9ca678 Scott Ullrich
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
200
		log_error($error);
201
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
202 62f96568 Ermal
		return null; // $timeleft
203 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
204 6c9ca678 Scott Ullrich
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
205
		log_error($error);
206
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
207 62f96568 Ermal
		return null; // $timeleft
208 6c9ca678 Scott Ullrich
	} else {
209
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
210
	}
211 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
212 79262830 Phil Davis
	if (!is_array($config['voucher'])) {
213 90df43c6 Ermal
		$config['voucher'] = array();
214 79262830 Phil Davis
	}
215 62f96568 Ermal
216 fac36dfd Ermal
	if (is_array($toreturn['voucher']) && is_array($toreturn['voucher']['roll'])) {
217 b4792bf8 Ermal
		$config['voucher'][$cpzone]['roll'] = $toreturn['voucher']['roll'];
218 6c9ca678 Scott Ullrich
		write_config("Captive Portal Voucher database synchronized with {$url}");
219 b4792bf8 Ermal
		voucher_configure_zone(true);
220 ef60dfa5 Ermal
		unset($toreturn['voucher']);
221 79262830 Phil Davis
	} else if (!isset($toreturn['timeleft'])) {
222 62f96568 Ermal
		return null;
223 79262830 Phil Davis
	}
224 f5c05fcc Ermal
225
	return $toreturn['timeleft'];
226 fa3ab36a Scott Ullrich
}
227
228 05771a24 Ermal
function voucher_expire($voucher_received) {
229 abaa7feb Ermal LUÇI
	global $g, $config, $cpzone, $cpzoneid;
230 05771a24 Ermal
231
	// XMLRPC Call over to the master Voucher node
232 79262830 Phil Davis
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
233 b4792bf8 Ermal
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
234
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
235
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
236
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
237 05771a24 Ermal
		xmlrpc_sync_voucher_expire($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
238
	}
239
240 90df43c6 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
241
242 05771a24 Ermal
	// read rolls into assoc array with rollid as key and minutes as value
243
	$tickets_per_roll = array();
244
	$minutes_per_roll = array();
245 b4792bf8 Ermal
	if (is_array($config['voucher'][$cpzone]['roll'])) {
246
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
247 05771a24 Ermal
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
248
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
249
		}
250
	}
251
252
	// split into an array. Useful for multiple vouchers given
253 79262830 Phil Davis
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received);
254 05771a24 Ermal
	$active_dirty = false;
255 5a6359ae bcyrill
	$unsetindexes = array();
256 2c85b316 Ermal
257 05771a24 Ermal
	// go through all received vouchers, check their valid and extract
258
	// Roll# and Ticket# using the external readvoucher binary
259
	foreach ($a_vouchers_received as $voucher) {
260
		$v = escapeshellarg($voucher);
261 79262830 Phil Davis
		if (strlen($voucher) < 3) {
262 05771a24 Ermal
			continue;   // seems too short to be a voucher!
263 79262830 Phil Davis
		}
264 05771a24 Ermal
265 15bec718 Ermal
		unset($output);
266
		$_gb = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v", $output);
267
		list($status, $roll, $nr) = explode(" ", $output[0]);
268 05771a24 Ermal
		if ($status == "OK") {
269 79262830 Phil Davis
			// check if we have this ticket on a registered roll for this ticket
270 05771a24 Ermal
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
271 79262830 Phil Davis
				// voucher is from a registered roll.
272
				if (!isset($active_vouchers[$roll])) {
273 05771a24 Ermal
					$active_vouchers[$roll] = voucher_read_active_db($roll);
274 79262830 Phil Davis
				}
275 05771a24 Ermal
				// valid voucher. Store roll# and ticket#
276
				if (!empty($active_vouchers[$roll][$voucher])) {
277
					$active_dirty = true;
278
					unset($active_vouchers[$roll][$voucher]);
279
				}
280
				// check if voucher already marked as used
281 79262830 Phil Davis
				if (!isset($bitstring[$roll])) {
282 05771a24 Ermal
					$bitstring[$roll] = voucher_read_used_db($roll);
283 79262830 Phil Davis
				}
284 05771a24 Ermal
				$pos = $nr >> 3; // divide by 8 -> octet
285
				$mask = 1 << ($nr % 8);
286
				// mark bit for this voucher as used
287 79262830 Phil Davis
				if (!(ord($bitstring[$roll][$pos]) & $mask)) {
288 05771a24 Ermal
					$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
289 79262830 Phil Davis
				}
290 05771a24 Ermal
				captiveportal_syslog("{$voucher} ({$roll}/{$nr}) forced to expire");
291 2c85b316 Ermal
292
				/* Check if this voucher has any active sessions */
293 26ee5aaf Ermal
				$cpentry = captiveportal_read_db("WHERE username = '{$voucher}'");
294 ea6cbc39 Gertjan
				if (!empty($cpentry) && !empty($cpentry[0])) {
295 086cf944 Phil Davis
					if (empty($cpzoneid) && !empty($config['captiveportal'][$cpzone])) {
296 abaa7feb Ermal LUÇI
						$cpzoneid = $config['captiveportal'][$cpzone]['zoneid'];
297 086cf944 Phil Davis
					}
298 ea6cbc39 Gertjan
					$cpentry = $cpentry[0];
299 086cf944 Phil Davis
					captiveportal_disconnect($cpentry, null, 13);
300
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "FORCLY TERMINATING VOUCHER {$voucher} SESSION");
301 5705c60a Renato Botelho
					$unsetindexes[] = $cpentry[5];
302 2c85b316 Ermal
				}
303 79262830 Phil Davis
			} else {
304 f416763b Phil Davis
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll");
305 79262830 Phil Davis
			}
306
		} else {
307 05771a24 Ermal
			// hmm, thats weird ... not what I expected
308 15bec718 Ermal
			captiveportal_syslog("$voucher invalid: {$output[0]}!!");
309 79262830 Phil Davis
		}
310 05771a24 Ermal
	}
311
312
	// Refresh active DBs
313
	if ($active_dirty == true) {
314 79262830 Phil Davis
		foreach ($active_vouchers as $roll => $active) {
315 05771a24 Ermal
			voucher_write_active_db($roll, $active);
316 79262830 Phil Davis
		}
317 15bec718 Ermal
		unset($active_vouchers);
318 5ebe85e9 Ermal
319 f416763b Phil Davis
		/* Trigger a sync of the vouchers on config */
320 5ebe85e9 Ermal
		send_event("service sync vouchers");
321 05771a24 Ermal
	}
322
323
	// Write back the used DB's
324
	if (is_array($bitstring)) {
325
		foreach ($bitstring as $roll => $used) {
326 79262830 Phil Davis
			if (is_array($used)) {
327
				foreach ($used as $u) {
328 05771a24 Ermal
					voucher_write_used_db($roll, base64_encode($u));
329 79262830 Phil Davis
				}
330 05771a24 Ermal
			} else {
331
				voucher_write_used_db($roll, base64_encode($used));
332
			}
333
		}
334 15bec718 Ermal
		unset($bitstring);
335 05771a24 Ermal
	}
336
337
	unlock($voucherlck);
338
339 2c85b316 Ermal
	/* Write database */
340 79262830 Phil Davis
	if (!empty($unsetindexes)) {
341 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
342 79262830 Phil Davis
	}
343 2c85b316 Ermal
344 05771a24 Ermal
	return true;
345
}
346
347 79262830 Phil Davis
/*
348 666bc4d1 Ermal
 * Authenticate a voucher and return the remaining time credit in minutes
349 336e3c1c Charlie
 * if $test is set, don't mark the voucher as used nor add it to the list
350
 * of active vouchers
351 666bc4d1 Ermal
 * If $test is set, simply test the voucher. Don't change anything
352
 * but return a more verbose error and result message back
353 336e3c1c Charlie
 */
354
function voucher_auth($voucher_received, $test = 0) {
355 b4792bf8 Ermal
	global $g, $config, $cpzone, $dbc;
356 336e3c1c Charlie
357 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
358 1a863be2 Ermal
		return 0;
359 79262830 Phil Davis
	}
360 1a863be2 Ermal
361 6c9ca678 Scott Ullrich
	// XMLRPC Call over to the master Voucher node
362 79262830 Phil Davis
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
363 b4792bf8 Ermal
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
364
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
365
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
366
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
367 6c9ca678 Scott Ullrich
		$remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
368
	}
369 fa3ab36a Scott Ullrich
370 90df43c6 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
371
372 7afb7ea9 Ermal
	// read rolls into assoc array with rollid as key and minutes as value
373
	$tickets_per_roll = array();
374
	$minutes_per_roll = array();
375 b4792bf8 Ermal
	if (is_array($config['voucher'][$cpzone]['roll'])) {
376
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
377 7afb7ea9 Ermal
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
378
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
379
		}
380
	}
381 336e3c1c Charlie
382 05771a24 Ermal
	// split into an array. Useful for multiple vouchers given
383 79262830 Phil Davis
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received);
384 05771a24 Ermal
	$error = 0;
385
	$test_result = array();     // used to display for voucher test option in GUI
386
	$total_minutes = 0;
387
	$first_voucher = "";
388
	$first_voucher_roll = 0;
389
390
	// go through all received vouchers, check their valid and extract
391
	// Roll# and Ticket# using the external readvoucher binary
392
	foreach ($a_vouchers_received as $voucher) {
393
		$v = escapeshellarg($voucher);
394 79262830 Phil Davis
		if (strlen($voucher) < 3) {
395 b08758c3 jim-p
			$test_result[] = "{$voucher} invalid: Too short!";
396
			captiveportal_syslog("{$voucher} invalid: Too short!");
397
			$error++;
398 05771a24 Ermal
			continue;   // seems too short to be a voucher!
399 79262830 Phil Davis
		}
400 05771a24 Ermal
401 b4792bf8 Ermal
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v");
402 05771a24 Ermal
		list($status, $roll, $nr) = explode(" ", $result);
403
		if ($status == "OK") {
404
			if (!$first_voucher) {
405
				// store first voucher. Thats the one we give the timecredit
406
				$first_voucher = $voucher;
407
				$first_voucher_roll = $roll;
408
			}
409 79262830 Phil Davis
			// check if we have this ticket on a registered roll for this ticket
410 05771a24 Ermal
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
411 79262830 Phil Davis
				// voucher is from a registered roll.
412
				if (!isset($active_vouchers[$roll])) {
413 05771a24 Ermal
					$active_vouchers[$roll] = voucher_read_active_db($roll);
414 79262830 Phil Davis
				}
415 05771a24 Ermal
				// valid voucher. Store roll# and ticket#
416
				if (!empty($active_vouchers[$roll][$voucher])) {
417 086cf944 Phil Davis
					list($timestamp, $minutes) = explode(",", $active_vouchers[$roll][$voucher]);
418 05771a24 Ermal
					// we have an already active voucher here.
419
					$remaining = intval((($timestamp + (60*$minutes)) - time())/60);
420 d8012adb Vinicius Coque
					$test_result[] = sprintf(gettext('%1$s (%2$s/%3$s) active and good for %4$d Minutes'), $voucher, $roll, $nr, $remaining);
421 05771a24 Ermal
					$total_minutes += $remaining;
422
				} else {
423
					// voucher not used. Check if ticket Id is on the roll (not too high)
424
					// and if the ticket is marked used.
425
					// check if voucher already marked as used
426 79262830 Phil Davis
					if (!isset($bitstring[$roll])) {
427 05771a24 Ermal
						$bitstring[$roll] = voucher_read_used_db($roll);
428 79262830 Phil Davis
					}
429 05771a24 Ermal
					$pos = $nr >> 3; // divide by 8 -> octet
430
					$mask = 1 << ($nr % 8);
431
					if (ord($bitstring[$roll][$pos]) & $mask) {
432
						$test_result[] = "$voucher ($roll/$nr) already used and expired";
433 34507786 Scott Ullrich
						captiveportal_syslog("$voucher ($roll/$nr) already used and expired");
434 05771a24 Ermal
						$total_minutes = -1;    // voucher expired
435
						$error++;
436
					} else {
437
						// mark bit for this voucher as used
438
						$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
439
						$test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
440
						$total_minutes += $minutes_per_roll[$roll];
441
					}
442
				}
443
			} else {
444 f416763b Phil Davis
				$test_result[] = "$voucher ($roll/$nr): not found on any registered Roll";
445
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll");
446 05771a24 Ermal
			}
447
		} else {
448
			// hmm, thats weird ... not what I expected
449
			$test_result[] = "$voucher invalid: $result !!";
450
			captiveportal_syslog("$voucher invalid: $result !!");
451
			$error++;
452
		}
453
	}
454 336e3c1c Charlie
455 05771a24 Ermal
	// if this was a test call, we're done. Return the result.
456
	if ($test) {
457
		if ($error) {
458 d8012adb Vinicius Coque
			$test_result[] = gettext("Access denied!");
459 05771a24 Ermal
		} else {
460 086cf944 Phil Davis
			$test_result[] = sprintf(gettext("Access granted for %d Minutes in total."), $total_minutes);
461 05771a24 Ermal
		}
462
		unlock($voucherlck);
463 336e3c1c Charlie
464 05771a24 Ermal
		return $test_result;
465
	}
466 336e3c1c Charlie
467 05771a24 Ermal
	// if we had an error (one of the vouchers is invalid), return 0.
468
	// Discussion: we could return the time remaining for good vouchers, but then
469
	// the user wouldn't know that he used at least one invalid voucher.
470
	if ($error) {
471 830c33be Scott Ullrich
		unlock($voucherlck);
472 79262830 Phil Davis
		if ($total_minutes > 0) {   // probably not needed, but want to make sure
473 05771a24 Ermal
			$total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
474 79262830 Phil Davis
		}
475 05771a24 Ermal
		return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
476
	}
477 336e3c1c Charlie
478 6c9ca678 Scott Ullrich
	// If we did a XMLRPC sync earlier check the timeleft
479 62f96568 Ermal
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
480 79262830 Phil Davis
		if (!is_null($remote_time_used)) {
481 62f96568 Ermal
			$total_minutes = $remote_time_used;
482 79262830 Phil Davis
		} else if ($remote_time_used < $total_minutes) {
483 f3512fca jim-p
			$total_minutes -= $remote_time_used;
484 79262830 Phil Davis
		}
485 62f96568 Ermal
	}
486 830c33be Scott Ullrich
487 05771a24 Ermal
	// All given vouchers were valid and this isn't simply a test.
488
	// Write back the used DB's
489 c14d9967 Scott Ullrich
	if (is_array($bitstring)) {
490
		foreach ($bitstring as $roll => $used) {
491 79262830 Phil Davis
			if (is_array($used)) {
492
				foreach ($used as $u) {
493 c14d9967 Scott Ullrich
					voucher_write_used_db($roll, base64_encode($u));
494 79262830 Phil Davis
				}
495 c14d9967 Scott Ullrich
			} else {
496
				voucher_write_used_db($roll, base64_encode($used));
497
			}
498
		}
499
	}
500 336e3c1c Charlie
501 05771a24 Ermal
	// Active DB: we only add the first voucher if multiple given
502
	// and give that one all the time credit. This allows the user to logout and
503
	// log in later using just the first voucher. It also keeps username limited
504
	// to one voucher and that voucher shows the correct time credit in 'active vouchers'
505
	if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) {
506
		list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]);
507
	} else {
508
		$timestamp = time();    // new voucher
509
		$minutes = $total_minutes;
510
	}
511 336e3c1c Charlie
512 05771a24 Ermal
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
513 80b86de5 Ermal
	voucher_write_active_db($first_voucher_roll, $active_vouchers[$first_voucher_roll]);
514 336e3c1c Charlie
515 f416763b Phil Davis
	/* Trigger a sync of the vouchers on config */
516 5ebe85e9 Ermal
	send_event("service sync vouchers");
517
518 05771a24 Ermal
	unlock($voucherlck);
519 336e3c1c Charlie
520 05771a24 Ermal
	return $total_minutes;
521 336e3c1c Charlie
}
522
523 351b6990 Ermal
function voucher_configure($sync = false) {
524 b4792bf8 Ermal
	global $config, $g, $cpzone;
525 336e3c1c Charlie
526 b4792bf8 Ermal
	if (is_array($config['voucher'])) {
527
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
528 79262830 Phil Davis
			if (platform_booting()) {
529
				echo gettext("Enabling voucher support... ");
530
			}
531 b4792bf8 Ermal
			$cpzone = $voucherzone;
532 abf421ce Ermal
			$error = voucher_configure_zone($sync);
533 285ef132 Ermal LUÇI
			if (platform_booting()) {
534 79262830 Phil Davis
				if ($error) {
535 abf421ce Ermal
					echo "error\n";
536 79262830 Phil Davis
				} else {
537 abf421ce Ermal
					echo "done\n";
538 79262830 Phil Davis
				}
539 abf421ce Ermal
			}
540 b4792bf8 Ermal
		}
541
	}
542
}
543
544
function voucher_configure_zone($sync = false) {
545
	global $config, $g, $cpzone;
546
547 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
548 eaca40df Ermal
		return 0;
549 79262830 Phil Davis
	}
550 336e3c1c Charlie
551 79262830 Phil Davis
	if ($sync == true) {
552
		captiveportal_syslog("Writing voucher db from sync data...");
553
	}
554 336e3c1c Charlie
555 b4792bf8 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
556 eaca40df Ermal
557 79262830 Phil Davis
	/* write public key used to verify vouchers */
558
	$pubkey = base64_decode($config['voucher'][$cpzone]['publickey']);
559
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w");
560
	if (!$fd) {
561
		captiveportal_syslog("Voucher error: cannot write voucher.public\n");
562
		unlock($voucherlck);
563
		return 1;
564
	}
565
	fwrite($fd, $pubkey);
566
	fclose($fd);
567
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600);
568
569
	/* write config file used by voucher binary to decode vouchers */
570
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w");
571
	if (!$fd) {
572
		printf(gettext("Error: cannot write voucher.cfg") . "\n");
573
		unlock($voucherlck);
574
		return 1;
575
	}
576
	fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n");
577
	fclose($fd);
578
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600);
579 c4ea3691 Ermal
	unlock($voucherlck);
580 336e3c1c Charlie
581 79262830 Phil Davis
	if ((platform_booting() || $sync == true) && is_array($config['voucher'][$cpzone]['roll'])) {
582 336e3c1c Charlie
583 b4792bf8 Ermal
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
584 336e3c1c Charlie
585 79262830 Phil Davis
		// create active and used DB per roll on ramdisk from config
586
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
587
588
			$roll = $rollent['number'];
589 351b8519 Luiz Otavio O Souza
			$len = ($rollent['count'] >> 3) + 1;
590
			if (strlen(base64_decode($rollent['used'])) != $len)
591
				$rollent['used'] = base64_encode(str_repeat("\000", $len));
592 79262830 Phil Davis
			voucher_write_used_db($roll, $rollent['used']);
593
			$minutes = $rollent['minutes'];
594
			$active_vouchers = array();
595
			$a_active = &$rollent['active'];
596
			if (is_array($a_active)) {
597
				foreach ($a_active as $activent) {
598
					$voucher = $activent['voucher'];
599
					$timestamp = $activent['timestamp'];
600
					$minutes = $activent['minutes'];
601
					// its tempting to check for expired timestamps, but during
602
					// bootup, we most likely don't have the correct time.
603
					$active_vouchers[$voucher] = "$timestamp,$minutes";
604
				}
605
			}
606
			voucher_write_active_db($roll, $active_vouchers);
607
		}
608 c4ea3691 Ermal
609
		unlock($voucherlck);
610 79262830 Phil Davis
	}
611 eaca40df Ermal
612
	return 0;
613 336e3c1c Charlie
}
614
615 79262830 Phil Davis
/* write bitstring of used vouchers to ramdisk.
616 336e3c1c Charlie
 * Bitstring must already be base64_encoded!
617
 */
618
function voucher_write_used_db($roll, $vdb) {
619 b4792bf8 Ermal
	global $g, $cpzone;
620 666bc4d1 Ermal
621 b4792bf8 Ermal
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w");
622 666bc4d1 Ermal
	if ($fd) {
623
		fwrite($fd, $vdb . "\n");
624
		fclose($fd);
625 79262830 Phil Davis
	} else {
626 b4792bf8 Ermal
		voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
627 79262830 Phil Davis
	}
628 336e3c1c Charlie
}
629
630
/* return assoc array of active vouchers with activation timestamp
631 79262830 Phil Davis
 * voucher is index.
632 336e3c1c Charlie
 */
633
function voucher_read_active_db($roll) {
634 b4792bf8 Ermal
	global $g, $cpzone;
635 666bc4d1 Ermal
636
	$active = array();
637
	$dirty = 0;
638 b4792bf8 Ermal
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db";
639 666bc4d1 Ermal
	if (file_exists($file)) {
640
		$fd = fopen($file, "r");
641
		if ($fd) {
642
			while (!feof($fd)) {
643
				$line = trim(fgets($fd));
644
				if ($line) {
645 086cf944 Phil Davis
					list($voucher, $timestamp, $minutes) = explode(",", $line); // voucher,timestamp
646 79262830 Phil Davis
					if ((($timestamp + (60*$minutes)) - time()) > 0) {
647 666bc4d1 Ermal
						$active[$voucher] = "$timestamp,$minutes";
648 79262830 Phil Davis
					} else {
649 666bc4d1 Ermal
						$dirty=1;
650 79262830 Phil Davis
					}
651 666bc4d1 Ermal
				}
652
			}
653
			fclose($fd);
654 5ebe85e9 Ermal
			if ($dirty) { // if we found expired entries, lets save our snapshot
655 666bc4d1 Ermal
				voucher_write_active_db($roll, $active);
656 5ebe85e9 Ermal
657 f416763b Phil Davis
				/* Trigger a sync of the vouchers on config */
658 5ebe85e9 Ermal
				send_event("service sync vouchers");
659
			}
660 666bc4d1 Ermal
		}
661
	}
662
	return $active;
663 336e3c1c Charlie
}
664
665
/* store array of active vouchers back to DB */
666
function voucher_write_active_db($roll, $active) {
667 79262830 Phil Davis
	global $g, $cpzone;
668 336e3c1c Charlie
669 79262830 Phil Davis
	if (!is_array($active)) {
670 05771a24 Ermal
		return;
671 79262830 Phil Davis
	}
672
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w");
673
	if ($fd) {
674
		foreach ($active as $voucher => $value) {
675
			fwrite($fd, "$voucher,$value\n");
676
		}
677
		fclose($fd);
678
	}
679 336e3c1c Charlie
}
680
681
/* return how many vouchers are marked used on a roll */
682
function voucher_used_count($roll) {
683 79262830 Phil Davis
	global $g, $cpzone;
684
685
	$bitstring = voucher_read_used_db($roll);
686
	$max = strlen($bitstring) * 8;
687
	$used = 0;
688
	for ($i = 1; $i <= $max; $i++) {
689
		// check if ticket already used or not.
690
		$pos = $i >> 3;            // divide by 8 -> octet
691
		$mask = 1 << ($i % 8);  // mask to test bit in octet
692
		if (ord($bitstring[$pos]) & $mask) {
693
			$used++;
694
		}
695
	}
696
	unset($bitstring);
697
698
	return $used;
699 336e3c1c Charlie
}
700
701
function voucher_read_used_db($roll) {
702 79262830 Phil Davis
	global $g, $cpzone;
703
704
	$vdb = "";
705
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db";
706
	if (file_exists($file)) {
707
		$fd = fopen($file, "r");
708
		if ($fd) {
709
			$vdb = trim(fgets($fd));
710
			fclose($fd);
711
		} else {
712
			voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
713
		}
714
	}
715
	return base64_decode($vdb);
716 336e3c1c Charlie
}
717
718
function voucher_unlink_db($roll) {
719 79262830 Phil Davis
	global $g, $cpzone;
720
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
721
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
722 336e3c1c Charlie
}
723
724
/* we share the log with captiveportal for now */
725
function voucher_log($priority, $message) {
726
727 79262830 Phil Davis
	$message = trim($message);
728
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
729 086cf944 Phil Davis
	syslog($priority, sprintf(gettext("Voucher: %s"), $message));
730 79262830 Phil Davis
	closelog();
731 336e3c1c Charlie
}
732
733
/* Save active and used voucher DB into XML config and write it to flash
734 5ebe85e9 Ermal
 * Called during reboot -> system_reboot_cleanup() and every active voucher change
735 336e3c1c Charlie
 */
736
function voucher_save_db_to_config() {
737 79262830 Phil Davis
	global $config, $g, $cpzone;
738 b4792bf8 Ermal
739
	if (is_array($config['voucher'])) {
740 573ae2d1 Chris Buechler
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
741 b4792bf8 Ermal
			$cpzone = $voucherzone;
742
			voucher_save_db_to_config_zone();
743
		}
744
	}
745
}
746
747 52bb3619 Cyrill Bannwart
function voucher_save_db_to_config_zone() {
748 79262830 Phil Davis
	global $config, $g, $cpzone;
749 336e3c1c Charlie
750 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
751
		return;   // no vouchers or don't want to save DB's
752
	}
753
754
	if (!is_array($config['voucher'][$cpzone]['roll'])) {
755
		return;
756
	}
757
758
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
759 c65c3a5d Ermal
760 79262830 Phil Davis
	// walk all active rolls and save runtime DB's to flash
761
	$a_roll = &$config['voucher'][$cpzone]['roll'];
762
	while (list($key, $value) = each($a_roll)) {
763
		$rollent = &$a_roll[$key];
764
		$roll = $rollent['number'];
765
		$bitmask = voucher_read_used_db($roll);
766
		$rollent['used'] = base64_encode($bitmask);
767
		$active_vouchers = voucher_read_active_db($roll);
768
		$db = array();
769 4d5bbdfb Scott Ullrich
		$dbi = 1;
770 79262830 Phil Davis
		foreach ($active_vouchers as $voucher => $line) {
771 086cf944 Phil Davis
			list($timestamp, $minutes) = explode(",", $line);
772 79262830 Phil Davis
			$activent['voucher'] = $voucher;
773
			$activent['timestamp'] = $timestamp;
774
			$activent['minutes'] = $minutes;
775
			$db["v{$dbi}"] = $activent;
776
			$dbi++;
777
		}
778
		$rollent['active'] = $db;
779
		unset($active_vouchers);
780
	}
781
782
	unlock($voucherlck);
783
784
	write_config("Syncing vouchers");
785
	return;
786 336e3c1c Charlie
}
787
788 2ee5fe9b Ermal Lu?i
?>