Project

General

Profile

Download (25.7 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
	$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
	$toreturn =  XML_RPC_Decode($resp->value());
94
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
	$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
	$toreturn =  XML_RPC_Decode($resp->value());
154
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
	$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
	$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 05771a24 Ermal
			continue;   // seems too short to be a voucher!
396 79262830 Phil Davis
		}
397 05771a24 Ermal
398 b4792bf8 Ermal
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v");
399 05771a24 Ermal
		list($status, $roll, $nr) = explode(" ", $result);
400
		if ($status == "OK") {
401
			if (!$first_voucher) {
402
				// store first voucher. Thats the one we give the timecredit
403
				$first_voucher = $voucher;
404
				$first_voucher_roll = $roll;
405
			}
406 79262830 Phil Davis
			// check if we have this ticket on a registered roll for this ticket
407 05771a24 Ermal
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
408 79262830 Phil Davis
				// voucher is from a registered roll.
409
				if (!isset($active_vouchers[$roll])) {
410 05771a24 Ermal
					$active_vouchers[$roll] = voucher_read_active_db($roll);
411 79262830 Phil Davis
				}
412 05771a24 Ermal
				// valid voucher. Store roll# and ticket#
413
				if (!empty($active_vouchers[$roll][$voucher])) {
414 086cf944 Phil Davis
					list($timestamp, $minutes) = explode(",", $active_vouchers[$roll][$voucher]);
415 05771a24 Ermal
					// we have an already active voucher here.
416
					$remaining = intval((($timestamp + (60*$minutes)) - time())/60);
417 d8012adb Vinicius Coque
					$test_result[] = sprintf(gettext('%1$s (%2$s/%3$s) active and good for %4$d Minutes'), $voucher, $roll, $nr, $remaining);
418 05771a24 Ermal
					$total_minutes += $remaining;
419
				} else {
420
					// voucher not used. Check if ticket Id is on the roll (not too high)
421
					// and if the ticket is marked used.
422
					// check if voucher already marked as used
423 79262830 Phil Davis
					if (!isset($bitstring[$roll])) {
424 05771a24 Ermal
						$bitstring[$roll] = voucher_read_used_db($roll);
425 79262830 Phil Davis
					}
426 05771a24 Ermal
					$pos = $nr >> 3; // divide by 8 -> octet
427
					$mask = 1 << ($nr % 8);
428
					if (ord($bitstring[$roll][$pos]) & $mask) {
429
						$test_result[] = "$voucher ($roll/$nr) already used and expired";
430 34507786 Scott Ullrich
						captiveportal_syslog("$voucher ($roll/$nr) already used and expired");
431 05771a24 Ermal
						$total_minutes = -1;    // voucher expired
432
						$error++;
433
					} else {
434
						// mark bit for this voucher as used
435
						$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
436
						$test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
437
						$total_minutes += $minutes_per_roll[$roll];
438
					}
439
				}
440
			} else {
441 f416763b Phil Davis
				$test_result[] = "$voucher ($roll/$nr): not found on any registered Roll";
442
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll");
443 05771a24 Ermal
			}
444
		} else {
445
			// hmm, thats weird ... not what I expected
446
			$test_result[] = "$voucher invalid: $result !!";
447
			captiveportal_syslog("$voucher invalid: $result !!");
448
			$error++;
449
		}
450
	}
451 336e3c1c Charlie
452 05771a24 Ermal
	// if this was a test call, we're done. Return the result.
453
	if ($test) {
454
		if ($error) {
455 d8012adb Vinicius Coque
			$test_result[] = gettext("Access denied!");
456 05771a24 Ermal
		} else {
457 086cf944 Phil Davis
			$test_result[] = sprintf(gettext("Access granted for %d Minutes in total."), $total_minutes);
458 05771a24 Ermal
		}
459
		unlock($voucherlck);
460 336e3c1c Charlie
461 05771a24 Ermal
		return $test_result;
462
	}
463 336e3c1c Charlie
464 05771a24 Ermal
	// if we had an error (one of the vouchers is invalid), return 0.
465
	// Discussion: we could return the time remaining for good vouchers, but then
466
	// the user wouldn't know that he used at least one invalid voucher.
467
	if ($error) {
468 830c33be Scott Ullrich
		unlock($voucherlck);
469 79262830 Phil Davis
		if ($total_minutes > 0) {   // probably not needed, but want to make sure
470 05771a24 Ermal
			$total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
471 79262830 Phil Davis
		}
472 05771a24 Ermal
		return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
473
	}
474 336e3c1c Charlie
475 6c9ca678 Scott Ullrich
	// If we did a XMLRPC sync earlier check the timeleft
476 62f96568 Ermal
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
477 79262830 Phil Davis
		if (!is_null($remote_time_used)) {
478 62f96568 Ermal
			$total_minutes = $remote_time_used;
479 79262830 Phil Davis
		} else if ($remote_time_used < $total_minutes) {
480 f3512fca jim-p
			$total_minutes -= $remote_time_used;
481 79262830 Phil Davis
		}
482 62f96568 Ermal
	}
483 830c33be Scott Ullrich
484 05771a24 Ermal
	// All given vouchers were valid and this isn't simply a test.
485
	// Write back the used DB's
486 c14d9967 Scott Ullrich
	if (is_array($bitstring)) {
487
		foreach ($bitstring as $roll => $used) {
488 79262830 Phil Davis
			if (is_array($used)) {
489
				foreach ($used as $u) {
490 c14d9967 Scott Ullrich
					voucher_write_used_db($roll, base64_encode($u));
491 79262830 Phil Davis
				}
492 c14d9967 Scott Ullrich
			} else {
493
				voucher_write_used_db($roll, base64_encode($used));
494
			}
495
		}
496
	}
497 336e3c1c Charlie
498 05771a24 Ermal
	// Active DB: we only add the first voucher if multiple given
499
	// and give that one all the time credit. This allows the user to logout and
500
	// log in later using just the first voucher. It also keeps username limited
501
	// to one voucher and that voucher shows the correct time credit in 'active vouchers'
502
	if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) {
503
		list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]);
504
	} else {
505
		$timestamp = time();    // new voucher
506
		$minutes = $total_minutes;
507
	}
508 336e3c1c Charlie
509 05771a24 Ermal
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
510 80b86de5 Ermal
	voucher_write_active_db($first_voucher_roll, $active_vouchers[$first_voucher_roll]);
511 336e3c1c Charlie
512 f416763b Phil Davis
	/* Trigger a sync of the vouchers on config */
513 5ebe85e9 Ermal
	send_event("service sync vouchers");
514
515 05771a24 Ermal
	unlock($voucherlck);
516 336e3c1c Charlie
517 05771a24 Ermal
	return $total_minutes;
518 336e3c1c Charlie
}
519
520 351b6990 Ermal
function voucher_configure($sync = false) {
521 b4792bf8 Ermal
	global $config, $g, $cpzone;
522 336e3c1c Charlie
523 b4792bf8 Ermal
	if (is_array($config['voucher'])) {
524
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
525 79262830 Phil Davis
			if (platform_booting()) {
526
				echo gettext("Enabling voucher support... ");
527
			}
528 b4792bf8 Ermal
			$cpzone = $voucherzone;
529 abf421ce Ermal
			$error = voucher_configure_zone($sync);
530 285ef132 Ermal LUÇI
			if (platform_booting()) {
531 79262830 Phil Davis
				if ($error) {
532 abf421ce Ermal
					echo "error\n";
533 79262830 Phil Davis
				} else {
534 abf421ce Ermal
					echo "done\n";
535 79262830 Phil Davis
				}
536 abf421ce Ermal
			}
537 b4792bf8 Ermal
		}
538
	}
539
}
540
541
function voucher_configure_zone($sync = false) {
542
	global $config, $g, $cpzone;
543
544 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
545 eaca40df Ermal
		return 0;
546 79262830 Phil Davis
	}
547 336e3c1c Charlie
548 79262830 Phil Davis
	if ($sync == true) {
549
		captiveportal_syslog("Writing voucher db from sync data...");
550
	}
551 336e3c1c Charlie
552 b4792bf8 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
553 eaca40df Ermal
554 79262830 Phil Davis
	/* write public key used to verify vouchers */
555
	$pubkey = base64_decode($config['voucher'][$cpzone]['publickey']);
556
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w");
557
	if (!$fd) {
558
		captiveportal_syslog("Voucher error: cannot write voucher.public\n");
559
		unlock($voucherlck);
560
		return 1;
561
	}
562
	fwrite($fd, $pubkey);
563
	fclose($fd);
564
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600);
565
566
	/* write config file used by voucher binary to decode vouchers */
567
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w");
568
	if (!$fd) {
569
		printf(gettext("Error: cannot write voucher.cfg") . "\n");
570
		unlock($voucherlck);
571
		return 1;
572
	}
573
	fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n");
574
	fclose($fd);
575
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600);
576 c4ea3691 Ermal
	unlock($voucherlck);
577 336e3c1c Charlie
578 79262830 Phil Davis
	if ((platform_booting() || $sync == true) && is_array($config['voucher'][$cpzone]['roll'])) {
579 336e3c1c Charlie
580 b4792bf8 Ermal
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
581 336e3c1c Charlie
582 79262830 Phil Davis
		// create active and used DB per roll on ramdisk from config
583
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
584
585
			$roll = $rollent['number'];
586
			voucher_write_used_db($roll, $rollent['used']);
587
			$minutes = $rollent['minutes'];
588
			$active_vouchers = array();
589
			$a_active = &$rollent['active'];
590
			if (is_array($a_active)) {
591
				foreach ($a_active as $activent) {
592
					$voucher = $activent['voucher'];
593
					$timestamp = $activent['timestamp'];
594
					$minutes = $activent['minutes'];
595
					// its tempting to check for expired timestamps, but during
596
					// bootup, we most likely don't have the correct time.
597
					$active_vouchers[$voucher] = "$timestamp,$minutes";
598
				}
599
			}
600
			voucher_write_active_db($roll, $active_vouchers);
601
		}
602 c4ea3691 Ermal
603
		unlock($voucherlck);
604 79262830 Phil Davis
	}
605 eaca40df Ermal
606
	return 0;
607 336e3c1c Charlie
}
608
609 79262830 Phil Davis
/* write bitstring of used vouchers to ramdisk.
610 336e3c1c Charlie
 * Bitstring must already be base64_encoded!
611
 */
612
function voucher_write_used_db($roll, $vdb) {
613 b4792bf8 Ermal
	global $g, $cpzone;
614 666bc4d1 Ermal
615 b4792bf8 Ermal
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w");
616 666bc4d1 Ermal
	if ($fd) {
617
		fwrite($fd, $vdb . "\n");
618
		fclose($fd);
619 79262830 Phil Davis
	} else {
620 b4792bf8 Ermal
		voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
621 79262830 Phil Davis
	}
622 336e3c1c Charlie
}
623
624
/* return assoc array of active vouchers with activation timestamp
625 79262830 Phil Davis
 * voucher is index.
626 336e3c1c Charlie
 */
627
function voucher_read_active_db($roll) {
628 b4792bf8 Ermal
	global $g, $cpzone;
629 666bc4d1 Ermal
630
	$active = array();
631
	$dirty = 0;
632 b4792bf8 Ermal
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db";
633 666bc4d1 Ermal
	if (file_exists($file)) {
634
		$fd = fopen($file, "r");
635
		if ($fd) {
636
			while (!feof($fd)) {
637
				$line = trim(fgets($fd));
638
				if ($line) {
639 086cf944 Phil Davis
					list($voucher, $timestamp, $minutes) = explode(",", $line); // voucher,timestamp
640 79262830 Phil Davis
					if ((($timestamp + (60*$minutes)) - time()) > 0) {
641 666bc4d1 Ermal
						$active[$voucher] = "$timestamp,$minutes";
642 79262830 Phil Davis
					} else {
643 666bc4d1 Ermal
						$dirty=1;
644 79262830 Phil Davis
					}
645 666bc4d1 Ermal
				}
646
			}
647
			fclose($fd);
648 5ebe85e9 Ermal
			if ($dirty) { // if we found expired entries, lets save our snapshot
649 666bc4d1 Ermal
				voucher_write_active_db($roll, $active);
650 5ebe85e9 Ermal
651 f416763b Phil Davis
				/* Trigger a sync of the vouchers on config */
652 5ebe85e9 Ermal
				send_event("service sync vouchers");
653
			}
654 666bc4d1 Ermal
		}
655
	}
656
	return $active;
657 336e3c1c Charlie
}
658
659
/* store array of active vouchers back to DB */
660
function voucher_write_active_db($roll, $active) {
661 79262830 Phil Davis
	global $g, $cpzone;
662 336e3c1c Charlie
663 79262830 Phil Davis
	if (!is_array($active)) {
664 05771a24 Ermal
		return;
665 79262830 Phil Davis
	}
666
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w");
667
	if ($fd) {
668
		foreach ($active as $voucher => $value) {
669
			fwrite($fd, "$voucher,$value\n");
670
		}
671
		fclose($fd);
672
	}
673 336e3c1c Charlie
}
674
675
/* return how many vouchers are marked used on a roll */
676
function voucher_used_count($roll) {
677 79262830 Phil Davis
	global $g, $cpzone;
678
679
	$bitstring = voucher_read_used_db($roll);
680
	$max = strlen($bitstring) * 8;
681
	$used = 0;
682
	for ($i = 1; $i <= $max; $i++) {
683
		// check if ticket already used or not.
684
		$pos = $i >> 3;            // divide by 8 -> octet
685
		$mask = 1 << ($i % 8);  // mask to test bit in octet
686
		if (ord($bitstring[$pos]) & $mask) {
687
			$used++;
688
		}
689
	}
690
	unset($bitstring);
691
692
	return $used;
693 336e3c1c Charlie
}
694
695
function voucher_read_used_db($roll) {
696 79262830 Phil Davis
	global $g, $cpzone;
697
698
	$vdb = "";
699
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db";
700
	if (file_exists($file)) {
701
		$fd = fopen($file, "r");
702
		if ($fd) {
703
			$vdb = trim(fgets($fd));
704
			fclose($fd);
705
		} else {
706
			voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
707
		}
708
	}
709
	return base64_decode($vdb);
710 336e3c1c Charlie
}
711
712
function voucher_unlink_db($roll) {
713 79262830 Phil Davis
	global $g, $cpzone;
714
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
715
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
716 336e3c1c Charlie
}
717
718
/* we share the log with captiveportal for now */
719
function voucher_log($priority, $message) {
720
721 79262830 Phil Davis
	$message = trim($message);
722
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
723 086cf944 Phil Davis
	syslog($priority, sprintf(gettext("Voucher: %s"), $message));
724 79262830 Phil Davis
	closelog();
725 336e3c1c Charlie
}
726
727
/* Save active and used voucher DB into XML config and write it to flash
728 5ebe85e9 Ermal
 * Called during reboot -> system_reboot_cleanup() and every active voucher change
729 336e3c1c Charlie
 */
730
function voucher_save_db_to_config() {
731 79262830 Phil Davis
	global $config, $g, $cpzone;
732 b4792bf8 Ermal
733
	if (is_array($config['voucher'])) {
734 573ae2d1 Chris Buechler
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
735 b4792bf8 Ermal
			$cpzone = $voucherzone;
736
			voucher_save_db_to_config_zone();
737
		}
738
	}
739
}
740
741 52bb3619 Cyrill Bannwart
function voucher_save_db_to_config_zone() {
742 79262830 Phil Davis
	global $config, $g, $cpzone;
743 336e3c1c Charlie
744 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
745
		return;   // no vouchers or don't want to save DB's
746
	}
747
748
	if (!is_array($config['voucher'][$cpzone]['roll'])) {
749
		return;
750
	}
751
752
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
753 c65c3a5d Ermal
754 79262830 Phil Davis
	// walk all active rolls and save runtime DB's to flash
755
	$a_roll = &$config['voucher'][$cpzone]['roll'];
756
	while (list($key, $value) = each($a_roll)) {
757
		$rollent = &$a_roll[$key];
758
		$roll = $rollent['number'];
759
		$bitmask = voucher_read_used_db($roll);
760
		$rollent['used'] = base64_encode($bitmask);
761
		$active_vouchers = voucher_read_active_db($roll);
762
		$db = array();
763 4d5bbdfb Scott Ullrich
		$dbi = 1;
764 79262830 Phil Davis
		foreach ($active_vouchers as $voucher => $line) {
765 086cf944 Phil Davis
			list($timestamp, $minutes) = explode(",", $line);
766 79262830 Phil Davis
			$activent['voucher'] = $voucher;
767
			$activent['timestamp'] = $timestamp;
768
			$activent['minutes'] = $minutes;
769
			$db["v{$dbi}"] = $activent;
770
			$dbi++;
771
		}
772
		$rollent['active'] = $db;
773
		unset($active_vouchers);
774
	}
775
776
	unlock($voucherlck);
777
778
	write_config("Syncing vouchers");
779
	return;
780 336e3c1c Charlie
}
781
782 2ee5fe9b Ermal Lu?i
?>