Project

General

Profile

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