Project

General

Profile

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