Project

General

Profile

Download (52.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	util.inc
4
	part of the pfSense project (http://www.pfsense.com)
5

    
6
	originally part of m0n0wall (http://m0n0.ch/wall)
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	All rights reserved.
9

    
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12

    
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15

    
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19

    
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30
*/
31

    
32
/*
33
	pfSense_BUILDER_BINARIES:	/bin/ps	/bin/kill	/usr/bin/killall	/sbin/ifconfig	/usr/bin/netstat
34
	pfSense_BUILDER_BINARIES:	/usr/bin/awk	/sbin/dmesg		/sbin/ping /usr/local/sbin/gzsig	/usr/sbin/arp
35
	pfSense_BUILDER_BINARIES:	/sbin/conscontrol	/sbin/devd	/bin/ps
36
	pfSense_MODULE:	utils
37
*/
38

    
39
/* kill a process by pid file */
40
function killbypid($pidfile) {
41
	return sigkillbypid($pidfile, "TERM");
42
}
43

    
44
function isvalidpid($pidfile) {
45
	$output = "";
46
	if (file_exists($pidfile)) {
47
		exec("/bin/pgrep -nF {$pidfile}", $output, $retval);
48
		return (intval($retval) == 0);
49
	}
50
	return false;
51
}
52

    
53
function is_process_running($process) {
54
	$output = "";
55
	exec("/bin/pgrep -anx {$process}", $output, $retval);
56

    
57
	return (intval($retval) == 0);
58
}
59

    
60
function isvalidproc($proc) {
61
	return is_process_running($proc);
62
}
63

    
64
/* sigkill a process by pid file */
65
/* return 1 for success and 0 for a failure */
66
function sigkillbypid($pidfile, $sig) {
67
	if (file_exists($pidfile))
68
		return mwexec("/bin/pkill -{$sig} -F {$pidfile}", true);
69

    
70
	return 0;
71
}
72

    
73
/* kill a process by name */
74
function sigkillbyname($procname, $sig) {
75
	if(isvalidproc($procname))
76
		return mwexec("/usr/bin/killall -{$sig} " . escapeshellarg($procname), true);
77
}
78

    
79
/* kill a process by name */
80
function killbyname($procname) {
81
	if(isvalidproc($procname))
82
		mwexec("/usr/bin/killall " . escapeshellarg($procname));
83
}
84

    
85
function is_subsystem_dirty($subsystem = "") {
86
	global $g;
87

    
88
	if ($subsystem == "")
89
		return false;
90

    
91
	if (file_exists("{$g['varrun_path']}/{$subsystem}.dirty"))
92
		return true;
93

    
94
	return false;
95
}
96

    
97
function mark_subsystem_dirty($subsystem = "") {
98
	global $g;
99

    
100
	if (!file_put_contents("{$g['varrun_path']}/{$subsystem}.dirty", "DIRTY"))
101
		log_error(sprintf(gettext("WARNING: Could not mark subsystem: %s dirty"), $subsystem));
102
}
103

    
104
function clear_subsystem_dirty($subsystem = "") {
105
	global $g;
106

    
107
	@unlink("{$g['varrun_path']}/{$subsystem}.dirty");
108
}
109

    
110
function config_lock() {
111
	return;
112
}
113
function config_unlock() {
114
	return;
115
}
116

    
117
/* lock configuration file */
118
function lock($lock, $op = LOCK_SH) {
119
	global $g, $cfglckkeyconsumers;
120
	if (!$lock)
121
		die(gettext("WARNING: You must give a name as parameter to lock() function."));
122
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
123
		@touch("{$g['tmp_path']}/{$lock}.lock");
124
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
125
	}
126
	$cfglckkeyconsumers++;
127
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
128
		if (flock($fp, $op))
129
			return $fp;
130
		else
131
			fclose($fp);
132
	}
133
}
134

    
135
function try_lock($lock, $timeout = 5) {
136
	global $g, $cfglckkeyconsumers;
137
	if (!$lock)
138
		die(gettext("WARNING: You must give a name as parameter to try_lock() function."));
139
	if (!file_exists("{$g['tmp_path']}/{$lock}.lock")) {
140
		@touch("{$g['tmp_path']}/{$lock}.lock");
141
		@chmod("{$g['tmp_path']}/{$lock}.lock", 0666);
142
	}
143
	$cfglckkeyconsumers++;
144
	if ($fp = fopen("{$g['tmp_path']}/{$lock}.lock", "w")) {
145
		$trycounter = 0;
146
		while(!flock($fp, LOCK_EX | LOCK_NB)) {
147
			if ($trycounter >= $timeout) {
148
				fclose($fp);
149
				return NULL;
150
			}
151
			sleep(1);
152
			$trycounter++;
153
		}
154

    
155
		return $fp;
156
	}
157

    
158
	return NULL;
159
}
160

    
161
/* unlock configuration file */
162
function unlock($cfglckkey = 0) {
163
	global $g, $cfglckkeyconsumers;
164
	flock($cfglckkey, LOCK_UN);
165
	fclose($cfglckkey);
166
	return;
167
}
168

    
169
/* unlock forcefully configuration file */
170
function unlock_force($lock) {
171
	global $g;
172

    
173
	@unlink("{$g['tmp_path']}/{$lock}.lock");
174
}
175

    
176
function send_event($cmd) {
177
	global $g;
178

    
179
	if(!isset($g['event_address']))
180
		$g['event_address'] = "unix:///var/run/check_reload_status";
181

    
182
	$try = 0;
183
	while ($try < 3) {
184
		$fd = @fsockopen($g['event_address']);
185
		if ($fd) {
186
			fwrite($fd, $cmd);
187
			$resp = fread($fd, 4096);
188
			if ($resp != "OK\n")
189
				log_error("send_event: sent {$cmd} got {$resp}");
190
			fclose($fd);
191
			$try = 3;
192
		} else if (!is_process_running("check_reload_status"))
193
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
194
		$try++;
195
	}
196
}
197

    
198
function send_multiple_events($cmds) {
199
	global $g;
200

    
201
	if(!isset($g['event_address']))
202
		$g['event_address'] = "unix:///var/run/check_reload_status";
203

    
204
	if (!is_array($cmds))
205
		return;
206

    
207
	while ($try < 3) {
208
		$fd = @fsockopen($g['event_address']);
209
		if ($fd) {
210
			foreach ($cmds as $cmd) {
211
				fwrite($fd, $cmd);
212
				$resp = fread($fd, 4096);
213
				if ($resp != "OK\n")
214
					log_error("send_event: sent {$cmd} got {$resp}");
215
			}
216
			fclose($fd);
217
			$try = 3;
218
		} else if (!is_process_running("check_reload_status"))
219
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
220
		$try++;
221
	}
222
}
223

    
224
function refcount_init($reference) {
225
	$shmid = @shmop_open($reference, "c", 0644, 10);
226
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
227
	@shmop_close($shmid);
228
}
229

    
230
function refcount_reference($reference) {
231
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
232
	$shm_lck = lock("shm{$reference}", LOCK_EX);
233
	try {
234
		/* NOTE: A warning is generated when shared memory does not exist */
235
		$shmid = @shmop_open($reference, "w", 0, 0);
236
		if (!$shmid) {
237
			refcount_init($reference);
238
			$shmid = @shmop_open($reference, "w", 0, 0);
239
			if (!$shmid) {
240
				log_error(gettext("Could not open shared memory {$reference}"));
241
				unlock($shm_lck);
242
				return;
243
			}
244
		}
245
		$shm_data = @shmop_read($shmid, 0, 10);
246
		$shm_data = intval($shm_data) + 1;
247
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
248
		@shmop_close($shmid);
249
		unlock($shm_lck);
250
	} catch (Exception $e) {
251
		log_error($e->getMessage());
252
		unlock($shm_lck);
253
	}
254

    
255
	return $shm_data;
256
}
257

    
258
function refcount_unreference($reference) {
259
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
260
	$shm_lck = lock("shm{$reference}", LOCK_EX);
261
	try {
262
		$shmid = @shmop_open($reference, "w", 0, 0);
263
		if (!$shmid) {
264
			refcount_init($reference);
265
			log_error(gettext("Could not open shared memory {$reference}"));
266
			unlock($shm_lck);
267
			return;
268
		}
269
		$shm_data = @shmop_read($shmid, 0, 10);
270
		$shm_data = intval($shm_data) - 1;
271
		if ($shm_data < 0) {
272
			//debug_backtrace();
273
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
274
		} else
275
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
276
		@shmop_close($shmid);
277
		unlock($shm_lck);
278
	} catch (Exception $e) {
279
		log_error($e->getMessage());
280
		unlock($shm_lck);
281
	}
282

    
283
	return $shm_data;
284
}
285

    
286
function refcount_read($reference) {
287
	/* This function just reads the current value of the refcount for information. */
288
	/* There is no need for locking. */
289
	$shmid = @shmop_open($reference, "a", 0, 0);
290
	if (!$shmid) {
291
		log_error(gettext("Could not open shared memory for read {$reference}"));
292
		return -1;
293
	}
294
	$shm_data = @shmop_read($shmid, 0, 10);
295
	@shmop_close($shmid);
296
	return $shm_data;
297
}
298

    
299
function is_module_loaded($module_name) {
300
	$running = `/sbin/kldstat | grep {$module_name} | /usr/bin/grep -v grep | /usr/bin/wc -l`;
301
	if (intval($running) >= 1)
302
		return true;
303
	else
304
		return false;
305
}
306

    
307
/* return the subnet address given a host address and a subnet bit count */
308
function gen_subnet($ipaddr, $bits) {
309
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
310
		return "";
311
	return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits));
312
}
313

    
314
/* return the subnet address given a host address and a subnet bit count */
315
function gen_subnetv6($ipaddr, $bits) {
316
	if (!is_ipaddrv6($ipaddr) || !is_numeric($bits))
317
		return "";
318

    
319
	$address = Net_IPv6::getNetmask($ipaddr, $bits);
320
	$address = Net_IPv6::compress($address);
321
	return $address;
322
}
323

    
324
/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */
325
function gen_subnet_max($ipaddr, $bits) {
326
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
327
		return "";
328

    
329
	return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
330
}
331

    
332
/* Generate end number for a given ipv6 subnet mask */
333
function gen_subnetv6_max($ipaddr, $bits) {
334
	if(!is_ipaddrv6($ipaddr))
335
		return false;
336

    
337
	$mask = Net_IPv6::getNetmask('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',$bits);
338

    
339
	$inet_ip = (binary)inet_pton($ipaddr);
340
	$inet_mask = (binary)inet_pton($mask);
341

    
342
	$inet_end = $inet_ip | ~$inet_mask;
343

    
344
	return (inet_ntop($inet_end));
345
}
346

    
347
/* returns a subnet mask (long given a bit count) */
348
function gen_subnet_mask_long($bits) {
349
	$sm = 0;
350
	for ($i = 0; $i < $bits; $i++) {
351
		$sm >>= 1;
352
		$sm |= 0x80000000;
353
	}
354
	return $sm;
355
}
356

    
357
/* same as above but returns a string */
358
function gen_subnet_mask($bits) {
359
	return long2ip(gen_subnet_mask_long($bits));
360
}
361

    
362
/* Convert long int to IP address, truncating to 32-bits. */
363
function long2ip32($ip) {
364
	return long2ip($ip & 0xFFFFFFFF);
365
}
366

    
367
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
368
function ip2long32($ip) {
369
	return ( ip2long($ip) & 0xFFFFFFFF );
370
}
371

    
372
/* Convert IP address to unsigned long int. */
373
function ip2ulong($ip) {
374
	return sprintf("%u", ip2long32($ip));
375
}
376

    
377
/* Find out how many IPs are contained within a given IP range
378
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
379
 */
380
function ip_range_size($startip, $endip) {
381
	if (is_ipaddr($startip) && is_ipaddr($endip)) {
382
		// Operate as unsigned long because otherwise it wouldn't work
383
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
384
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
385
	}
386
	return -1;
387
}
388

    
389
/* Find the smallest possible subnet mask which can contain a given number of IPs
390
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
391
 */
392
function find_smallest_cidr($number) {
393
	$smallest = 1;
394
	for ($b=32; $b > 0; $b--) {
395
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
396
	}
397
	return (32-$smallest);
398
}
399

    
400
/* Return the previous IP address before the given address */
401
function ip_before($ip) {
402
	return long2ip32(ip2long($ip)-1);
403
}
404

    
405
/* Return the next IP address after the given address */
406
function ip_after($ip) {
407
	return long2ip32(ip2long($ip)+1);
408
}
409

    
410
/* Return true if the first IP is 'before' the second */
411
function ip_less_than($ip1, $ip2) {
412
	// Compare as unsigned long because otherwise it wouldn't work when
413
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
414
	return ip2ulong($ip1) < ip2ulong($ip2);
415
}
416

    
417
/* Return true if the first IP is 'after' the second */
418
function ip_greater_than($ip1, $ip2) {
419
	// Compare as unsigned long because otherwise it wouldn't work
420
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
421
	return ip2ulong($ip1) > ip2ulong($ip2);
422
}
423

    
424
/* Convert a range of IPs to an array of subnets which can contain the range. */
425
function ip_range_to_subnet_array($startip, $endip) {
426
	if (!is_ipaddr($startip) || !is_ipaddr($endip)) {
427
		return array();
428
	}
429

    
430
	// Container for subnets within this range.
431
	$rangesubnets = array();
432

    
433
	// Figure out what the smallest subnet is that holds the number of IPs in the given range.
434
	$cidr = find_smallest_cidr(ip_range_size($startip, $endip));
435

    
436
	// Loop here to reduce subnet size and retest as needed. We need to make sure
437
	//   that the target subnet is wholly contained between $startip and $endip.
438
	for ($cidr; $cidr <= 32; $cidr++) {
439
		// Find the network and broadcast addresses for the subnet being tested.
440
		$targetsub_min = gen_subnet($startip, $cidr);
441
		$targetsub_max = gen_subnet_max($startip, $cidr);
442

    
443
		// Check best case where the range is exactly one subnet.
444
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
445
			// Hooray, the range is exactly this subnet!
446
			return array("{$startip}/{$cidr}");
447
		}
448

    
449
		// These remaining scenarios will find a subnet that uses the largest
450
		//  chunk possible of the range being tested, and leave the rest to be
451
		//  tested recursively after the loop.
452

    
453
		// Check if the subnet begins with $startip and ends before $endip
454
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
455
			break;
456
		}
457

    
458
		// Check if the subnet ends at $endip and starts after $startip
459
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
460
			break;
461
		}
462

    
463
		// Check if the subnet is between $startip and $endip
464
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
465
			break;
466
		}
467
	}
468

    
469
	// Some logic that will recursivly search from $startip to the first IP before the start of the subnet we just found.
470
	// NOTE: This may never be hit, the way the above algo turned out, but is left for completeness.
471
	if ($startip != $targetsub_min) {
472
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min)));
473
	}
474

    
475
	// Add in the subnet we found before, to preserve ordering
476
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
477

    
478
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
479
	if ($endip != $targetsub_max) {
480
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
481
	}
482
	return $rangesubnets;
483
}
484

    
485
function is_iprange($range) {
486
	if (substr_count($range, '-') != 1) {
487
		return false;
488
	}
489
	list($ip1, $ip2) = explode ('-', $range);
490
	return (is_ipaddr($ip1) && is_ipaddr($ip2));
491
}
492

    
493
function is_numericint($arg) {
494
	return (preg_match("/[^0-9]/", $arg) ? false : true);
495
}
496

    
497

    
498
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 */
499
function is_ipaddr($ipaddr) {
500
	if(is_ipaddrv4($ipaddr)) {
501
		return true;
502
	}
503
	if(is_ipaddrv6($ipaddr)) {
504
		return true;
505
	}
506
	return false;
507
}
508

    
509
/* returns true if $ipaddr is a valid IPv6 address */
510
function is_ipaddrv6($ipaddr) {
511
	if (!is_string($ipaddr) || empty($ipaddr))
512
		return false;
513
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
514
		$tmpip = explode("%", $ipaddr);
515
		$ipaddr = $tmpip[0];
516
	}
517
	return Net_IPv6::checkIPv6($ipaddr);
518
}
519

    
520
/* returns true if $ipaddr is a valid dotted IPv4 address */
521
function is_ipaddrv4($ipaddr) {
522
	if (!is_string($ipaddr) || empty($ipaddr))
523
		return false;
524

    
525
	$ip_long = ip2long($ipaddr);
526
	$ip_reverse = long2ip32($ip_long);
527

    
528
	if ($ipaddr == $ip_reverse)
529
		return true;
530
	else
531
		return false;
532
}
533

    
534
/* returns true if $ipaddr is a valid linklocal address */
535
function is_linklocal($ipaddr) {
536
	return (substr($ipaddr, 0, 5) == "fe80:");
537
}
538

    
539
/* returns scope of a linklocal address */
540
function get_ll_scope($addr) {
541
	if (!is_linklocal($addr) || !strstr($addr, "%"))
542
		return "";
543
	list ($ll, $scope) = explode("%", $addr);
544
	return $scope;
545
}
546

    
547
/* returns true if $ipaddr is a valid literal IPv6 address */
548
function is_literalipaddrv6($ipaddr) {
549
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
550
		$ipaddr = $match[1];
551
	else
552
		return false;
553

    
554
	return is_ipaddrv6($ipaddr);
555
}
556

    
557
function is_ipaddrwithport($ipport) {
558
	$parts = explode(":", $ipport);
559
	$port = array_pop($parts);
560
	if (count($parts) == 1) {
561
		return is_ipaddrv4($parts[0]) && is_port($port);
562
	} elseif (count($parts) > 1) {
563
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
564
	} else {
565
		return false;
566
	}
567
}
568

    
569
function is_hostnamewithport($hostport) {
570
	$parts = explode(":", $hostport);
571
	$port = array_pop($parts);
572
	if (count($parts) == 1) {
573
		return is_hostname($parts[0]) && is_port($port);
574
	} else {
575
		return false;
576
	}
577
}
578

    
579
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
580
function is_ipaddroralias($ipaddr) {
581
	global $config;
582

    
583
	if (is_alias($ipaddr)) {
584
		if (is_array($config['aliases']['alias'])) {
585
			foreach ($config['aliases']['alias'] as $alias) {
586
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type']))
587
					return true;
588
			}
589
		}
590
		return false;
591
	} else
592
		return is_ipaddr($ipaddr);
593

    
594
}
595

    
596
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format */
597
function is_subnet($subnet) {
598
	if(is_subnetv4($subnet)) {
599
		return true;
600
	}
601
	if(is_subnetv6($subnet)) {
602
		return true;
603
	}
604
	return false;
605
}
606

    
607
/* returns true if $subnet is a valid IPv4 subnet in CIDR format */
608
function is_subnetv4($subnet) {
609
	if (!is_string($subnet))
610
		return false;
611

    
612
	list($hp,$np) = explode('/', $subnet);
613

    
614
	if (!is_ipaddrv4($hp))
615
		return false;
616

    
617
	if (!is_numeric($np) || ($np < 1) || ($np > 32))
618
		return false;
619

    
620
	return true;
621
}
622

    
623
/* returns true if $subnet is a valid IPv6 subnet in CIDR format */
624
function is_subnetv6($subnet) {
625
	if (!is_string($subnet))
626
		return false;
627

    
628
	list($hp,$np) = explode('/', $subnet);
629

    
630
	if (!is_ipaddrv6($hp))
631
		return false;
632

    
633
	if (!is_numeric($np) || ($np < 1) || ($np > 128))
634
		return false;
635

    
636
	return true;
637
}
638

    
639

    
640
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
641
function is_subnetoralias($subnet) {
642
	global $aliastable;
643

    
644
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
645
		return true;
646
	else
647
		return is_subnet($subnet);
648
}
649

    
650
/* returns true if $hostname is a valid hostname */
651
function is_hostname($hostname) {
652
	if (!is_string($hostname))
653
		return false;
654

    
655
	if (preg_match('/^(?:(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])\.)*(?:[a-z0-9_]|[a-z0-9_][a-z0-9_\-]*[a-z0-9_])$/i', $hostname))
656
		return true;
657
	else
658
		return false;
659
}
660

    
661
/* returns true if $domain is a valid domain name */
662
function is_domain($domain) {
663
	if (!is_string($domain))
664
		return false;
665

    
666
	if (preg_match('/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i', $domain))
667
		return true;
668
	else
669
		return false;
670
}
671

    
672
/* returns true if $macaddr is a valid MAC address */
673
function is_macaddr($macaddr, $partial=false) {
674
	$repeat = ($partial) ? '1,5' : '5';
675
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
676
}
677

    
678
/* returns true if $name is a valid name for an alias */
679
/* returns NULL if a reserved word is used */
680
function is_validaliasname($name) {
681
	/* Array of reserved words */
682
	$reserved = array("port", "pass");
683
	if (in_array($name, $reserved, true))
684
		return; /* return NULL */
685
	if (!preg_match("/[^a-zA-Z0-9_]/", $name) && (strlen($name) < 32))
686
		return true;
687
	else
688
		return false;
689
}
690

    
691
/* returns true if $port is a valid TCP/UDP port */
692
function is_port($port) {
693
	$tmpports = explode(":", $port);
694
	foreach($tmpports as $tmpport) {
695
		if (getservbyname($tmpport, "tcp") || getservbyname($tmpport, "udp"))
696
			continue;
697
		if (!ctype_digit($tmpport))
698
			return false;
699
		else if ((intval($tmpport) < 1) || (intval($tmpport) > 65535))
700
			return false;
701
	}
702
	return true;
703
}
704

    
705
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
706
function is_portrange($portrange) {
707
	$ports = explode(":", $portrange);
708

    
709
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
710
}
711

    
712
/* returns true if $port is a valid port number or an alias thereof */
713
function is_portoralias($port) {
714
	global $config;
715

    
716
	if (is_alias($port)) {
717
		if (is_array($config['aliases']['alias'])) {
718
			foreach ($config['aliases']['alias'] as $alias) {
719
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type']))
720
					return true;
721
				}
722
			}
723
			return false;
724
	} else
725
		return is_port($port);
726
}
727

    
728
/* create ranges of sequential port numbers (200:215) and remove duplicates */
729
function group_ports($ports) {
730
	if (!is_array($ports) || empty($ports))
731
		return;
732

    
733
	$uniq = array();
734
	foreach ($ports as $port) {
735
		if (is_portrange($port)) {
736
			list($begin, $end) = explode(":", $port);
737
			if ($begin > $end) {
738
				$aux = $begin;
739
				$begin = $end;
740
				$end = $aux;
741
			}
742
			for ($i = $begin; $i <= $end; $i++)
743
				if (!in_array($i, $uniq))
744
					$uniq[] = $i;
745
		} else if (is_port($port)) {
746
			if (!in_array($port, $uniq))
747
				$uniq[] = $port;
748
		}
749
	}
750
	sort($uniq, SORT_NUMERIC);
751

    
752
	$result = array();
753
	foreach ($uniq as $idx => $port) {
754
		if ($idx == 0) {
755
			$result[] = $port;
756
			continue;
757
		}
758

    
759
		$last = end($result);
760
		if (is_portrange($last))
761
			list($begin, $end) = explode(":", $last);
762
		else
763
			$begin = $end = $last;
764

    
765
		if ($port == ($end+1)) {
766
			$end++;
767
			$result[count($result)-1] = "{$begin}:{$end}";
768
		} else {
769
			$result[] = $port;
770
		}
771
	}
772

    
773
	return $result;
774
}
775

    
776
/* returns true if $val is a valid shaper bandwidth value */
777
function is_valid_shaperbw($val) {
778
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
779
}
780

    
781
/* returns true if $test is in the range between $start and $end */
782
function is_inrange_v4($test, $start, $end) {
783
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
784
		return true;
785
	else
786
		return false;
787
}
788

    
789
/* returns true if $test is in the range between $start and $end */
790
function is_inrange_v6($test, $start, $end) {
791
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
792
		return true;
793
	else
794
		return false;
795
}
796

    
797
/* return the configured carp interface list */
798
function get_configured_carp_interface_list() {
799
	global $config;
800

    
801
	$iflist = array();
802

    
803
	if(is_array($config['virtualip']['vip'])) {
804
		$viparr = &$config['virtualip']['vip'];
805
		foreach ($viparr as $vip) {
806
			switch ($vip['mode']) {
807
			case "carp":
808
				$vipif = "{$vip['interface']}_vip{$vip['vhid']}";
809
				$iflist[$vipif] = $vip['subnet'];
810
				break;
811
			}
812
		}
813
	}
814

    
815
	return $iflist;
816
}
817

    
818
/* return the configured IP aliases list */
819
function get_configured_ip_aliases_list($returnfullentry = false) {
820
	global $config;
821

    
822
	$alias_list=array();
823

    
824
	if(is_array($config['virtualip']['vip'])) {
825
		$viparr = &$config['virtualip']['vip'];
826
		foreach ($viparr as $vip) {
827
			if ($vip['mode']=="ipalias") {
828
				if ($returnfullentry)
829
					$alias_list[$vip['subnet']] = $vip;
830
				else
831
					$alias_list[$vip['subnet']] = $vip['interface'];
832
			}
833
		}
834
	}
835

    
836
	return $alias_list;
837
}
838

    
839
/* return all configured aliases list (IP, carp, proxyarp and other) */
840
function get_configured_vips_list() {
841
	global $config;
842

    
843
	$alias_list=array();
844

    
845
	if(is_array($config['virtualip']['vip'])) {
846
		$viparr = &$config['virtualip']['vip'];
847
		foreach ($viparr as $vip)
848
			$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
849
	}
850

    
851
	return $alias_list;
852
}
853

    
854
/* comparison function for sorting by the order in which interfaces are normally created */
855
function compare_interface_friendly_names($a, $b) {
856
	if ($a == $b)
857
		return 0;
858
	else if ($a == 'wan')
859
		return -1;
860
	else if ($b == 'wan')
861
		return 1;
862
	else if ($a == 'lan')
863
		return -1;
864
	else if ($b == 'lan')
865
		return 1;
866

    
867
	return strnatcmp($a, $b);
868
}
869

    
870
/* return the configured interfaces list. */
871
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
872
	global $config;
873

    
874
	$iflist = array();
875

    
876
	/* if list */
877
	foreach($config['interfaces'] as $if => $ifdetail) {
878
		if ($only_opt && ($if == "wan" || $if == "lan"))
879
			continue;
880
		if (isset($ifdetail['enable']) || $withdisabled == true)
881
			$iflist[$if] = $if;
882
	}
883

    
884
	return $iflist;
885
}
886

    
887
/* return the configured interfaces list. */
888
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
889
	global $config;
890

    
891
	$iflist = array();
892

    
893
	/* if list */
894
	foreach($config['interfaces'] as $if => $ifdetail) {
895
		if ($only_opt && ($if == "wan" || $if == "lan"))
896
			continue;
897
		if (isset($ifdetail['enable']) || $withdisabled == true) {
898
			$tmpif = get_real_interface($if);
899
			if (!empty($tmpif))
900
				$iflist[$tmpif] = $if;
901
		}
902
	}
903

    
904
	return $iflist;
905
}
906

    
907
/* return the configured interfaces list with their description. */
908
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
909
	global $config;
910

    
911
	$iflist = array();
912

    
913
	/* if list */
914
	foreach($config['interfaces'] as $if => $ifdetail) {
915
		if ($only_opt && ($if == "wan" || $if == "lan"))
916
			continue;
917
		if (isset($ifdetail['enable']) || $withdisabled == true) {
918
			if(empty($ifdetail['descr']))
919
				$iflist[$if] = strtoupper($if);
920
			else
921
				$iflist[$if] = strtoupper($ifdetail['descr']);
922
		}
923
	}
924

    
925
	return $iflist;
926
}
927

    
928
/*
929
 *   get_configured_ip_addresses() - Return a list of all configured
930
 *   interfaces IP Addresses
931
 *
932
 */
933
function get_configured_ip_addresses() {
934
	global $config;
935

    
936
	if (!function_exists('get_interface_ip'))
937
		require_once("interfaces.inc");
938
	$ip_array = array();
939
	$interfaces = get_configured_interface_list();
940
	if (is_array($interfaces)) {
941
		foreach($interfaces as $int) {
942
			$ipaddr = get_interface_ip($int);
943
			$ip_array[$int] = $ipaddr;
944
		}
945
	}
946
	$interfaces = get_configured_carp_interface_list();
947
	if (is_array($interfaces))
948
		foreach($interfaces as $int => $ipaddr)
949
			$ip_array[$int] = $ipaddr;
950

    
951
	/* pppoe server */
952
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
953
		foreach($config['pppoes']['pppoe'] as $pppoe) {
954
			if ($pppoe['mode'] == "server") {
955
				if(is_ipaddr($pppoe['localip'])) {
956
					$int = "pppoes". $pppoe['pppoeid'];
957
					$ip_array[$int] = $pppoe['localip'];
958
				}
959
			}
960
		}
961
	}
962

    
963
	return $ip_array;
964
}
965

    
966
/*
967
 *   get_configured_ipv6_addresses() - Return a list of all configured
968
 *   interfaces IPv6 Addresses
969
 *
970
 */
971
function get_configured_ipv6_addresses() {
972
	require_once("interfaces.inc");
973
	$ipv6_array = array();
974
	$interfaces = get_configured_interface_list();
975
	if(is_array($interfaces)) {
976
		foreach($interfaces as $int) {
977
			$ipaddrv6 = get_interface_ipv6($int);
978
			$ipv6_array[$int] = $ipaddrv6;
979
		}
980
	}
981
	$interfaces = get_configured_carp_interface_list();
982
	if(is_array($interfaces))
983
		foreach($interfaces as $int => $ipaddrv6)
984
			$ipv6_array[$int] = $ipaddrv6;
985
	return $ipv6_array;
986
}
987

    
988
/*
989
 *   get_interface_list() - Return a list of all physical interfaces
990
 *   along with MAC and status.
991
 *
992
 *   $mode = "active" - use ifconfig -lu
993
 *           "media"  - use ifconfig to check physical connection
994
 *			status (much slower)
995
 */
996
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
997
	global $config;
998
	$upints = array();
999
	/* get a list of virtual interface types */
1000
	if(!$vfaces) {
1001
		$vfaces = array (
1002
				'bridge',
1003
				'ppp',
1004
				'pppoe',
1005
				'pptp',
1006
				'l2tp',
1007
				'sl',
1008
				'gif',
1009
				'gre',
1010
				'faith',
1011
				'lo',
1012
				'ng',
1013
				'_vlan',
1014
				'_wlan',
1015
				'pflog',
1016
				'plip',
1017
				'pfsync',
1018
				'enc',
1019
				'tun',
1020
				'carp',
1021
				'lagg',
1022
				'vip',
1023
				'ipfw'
1024
		);
1025
	}
1026
	switch($mode) {
1027
	case "active":
1028
		$upints = pfSense_interface_listget(IFF_UP);
1029
		break;
1030
	case "media":
1031
		$intlist = pfSense_interface_listget();
1032
		$ifconfig = "";
1033
		exec("/sbin/ifconfig -a", $ifconfig);
1034
		$regexp = '/(' . implode('|', $intlist) . '):\s/';
1035
		$ifstatus = preg_grep('/status:/', $ifconfig);
1036
		foreach($ifstatus as $status) {
1037
			$int = array_shift($intlist);
1038
			if(stristr($status, "active")) $upints[] = $int;
1039
		}
1040
		break;
1041
	default:
1042
		$upints = pfSense_interface_listget();
1043
		break;
1044
	}
1045
	/* build interface list with netstat */
1046
	$linkinfo = "";
1047
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1048
	array_shift($linkinfo);
1049
	/* build ip address list with netstat */
1050
	$ipinfo = "";
1051
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1052
	array_shift($ipinfo);
1053
	foreach($linkinfo as $link) {
1054
		$friendly = "";
1055
		$alink = explode(" ", $link);
1056
		$ifname = rtrim(trim($alink[0]), '*');
1057
		/* trim out all numbers before checking for vfaces */
1058
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1059
			!stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1060
			$toput = array(
1061
					"mac" => trim($alink[1]),
1062
					"up" => in_array($ifname, $upints)
1063
				);
1064
			foreach($ipinfo as $ip) {
1065
				$aip = explode(" ", $ip);
1066
				if($aip[0] == $ifname) {
1067
					$toput['ipaddr'] = $aip[1];
1068
				}
1069
			}
1070
			if (is_array($config['interfaces'])) {
1071
				foreach($config['interfaces'] as $name => $int)
1072
					if($int['if'] == $ifname) $friendly = $name;
1073
			}
1074
			switch($keyby) {
1075
			case "physical":
1076
				if($friendly != "") {
1077
					$toput['friendly'] = $friendly;
1078
				}
1079
				$dmesg_arr = array();
1080
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1081
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1082
				$toput['dmesg'] = $dmesg[1][0];
1083
				$iflist[$ifname] = $toput;
1084
				break;
1085
			case "ppp":
1086

    
1087
			case "friendly":
1088
				if($friendly != "") {
1089
					$toput['if'] = $ifname;
1090
					$iflist[$friendly] = $toput;
1091
				}
1092
				break;
1093
			}
1094
		}
1095
	}
1096
	return $iflist;
1097
}
1098

    
1099
/****f* util/log_error
1100
* NAME
1101
*   log_error  - Sends a string to syslog.
1102
* INPUTS
1103
*   $error     - string containing the syslog message.
1104
* RESULT
1105
*   null
1106
******/
1107
function log_error($error) {
1108
	global $g;
1109
	$page = $_SERVER['SCRIPT_NAME'];
1110
	if (empty($page)) {
1111
		$files = get_included_files();
1112
		$page = basename($files[0]);
1113
	}
1114
	syslog(LOG_ERR, "$page: $error");
1115
	if ($g['debug'])
1116
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1117
	return;
1118
}
1119

    
1120
/****f* util/log_auth
1121
* NAME
1122
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1123
* INPUTS
1124
*   $error     - string containing the syslog message.
1125
* RESULT
1126
*   null
1127
******/
1128
function log_auth($error) {
1129
	global $g;
1130
	$page = $_SERVER['SCRIPT_NAME'];
1131
	syslog(LOG_AUTH, "$page: $error");
1132
	if ($g['debug'])
1133
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1134
	return;
1135
}
1136

    
1137
/****f* util/exec_command
1138
 * NAME
1139
 *   exec_command - Execute a command and return a string of the result.
1140
 * INPUTS
1141
 *   $command   - String of the command to be executed.
1142
 * RESULT
1143
 *   String containing the command's result.
1144
 * NOTES
1145
 *   This function returns the command's stdout and stderr.
1146
 ******/
1147
function exec_command($command) {
1148
	$output = array();
1149
	exec($command . ' 2>&1 ', $output);
1150
	return(implode("\n", $output));
1151
}
1152

    
1153
/* wrapper for exec() */
1154
function mwexec($command, $mute = false, $clearsigmask = false) {
1155
	global $g;
1156

    
1157
	if ($g['debug']) {
1158
		if (!$_SERVER['REMOTE_ADDR'])
1159
			echo "mwexec(): $command\n";
1160
	}
1161
	$oarr = array();
1162
	$retval = 0;
1163

    
1164
	if ($clearsigmask) {
1165
		$oldset = array();
1166
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1167
	}
1168
	$garbage = exec("$command 2>&1", $oarr, $retval);
1169
	if ($clearsigmask) {
1170
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1171
	}
1172

    
1173
	if(isset($config['system']['developerspew']))
1174
		$mute = false;
1175
	if(($retval <> 0) && ($mute === false)) {
1176
		$output = implode(" ", $oarr);
1177
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1178
	}
1179
	return $retval;
1180
}
1181

    
1182
/* wrapper for exec() in background */
1183
function mwexec_bg($command, $clearsigmask = false) {
1184
	global $g;
1185

    
1186
	if ($g['debug']) {
1187
		if (!$_SERVER['REMOTE_ADDR'])
1188
			echo "mwexec(): $command\n";
1189
	}
1190

    
1191
	if ($clearsigmask) {
1192
		$oldset = array();
1193
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1194
	}
1195
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1196
	if ($clearsigmask) {
1197
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1198
	}
1199
	unset($_gb);
1200
}
1201

    
1202
/* unlink a file, if it exists */
1203
function unlink_if_exists($fn) {
1204
	$to_do = glob($fn);
1205
	if(is_array($to_do)) {
1206
		foreach($to_do as $filename)
1207
			@unlink($filename);
1208
	} else {
1209
		@unlink($fn);
1210
	}
1211
}
1212
/* make a global alias table (for faster lookups) */
1213
function alias_make_table($config) {
1214
	global $aliastable;
1215

    
1216
	$aliastable = array();
1217

    
1218
	if (is_array($config['aliases']['alias'])) {
1219
		foreach ($config['aliases']['alias'] as $alias) {
1220
			if ($alias['name'])
1221
				$aliastable[$alias['name']] = $alias['address'];
1222
		}
1223
	}
1224
}
1225

    
1226
/* check if an alias exists */
1227
function is_alias($name) {
1228
	global $aliastable;
1229

    
1230
	return isset($aliastable[$name]);
1231
}
1232

    
1233
function alias_get_type($name) {
1234
	global $config;
1235

    
1236
	if (is_array($config['aliases']['alias'])) {
1237
		foreach ($config['aliases']['alias'] as $alias) {
1238
			if ($name == $alias['name'])
1239
				return $alias['type'];
1240
		}
1241
	}
1242

    
1243
	return "";
1244
}
1245

    
1246
/* expand a host or network alias, if necessary */
1247
function alias_expand($name) {
1248
	global $aliastable;
1249

    
1250
	if (isset($aliastable[$name]))
1251
		return "\${$name}";
1252
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name))
1253
		return "{$name}";
1254
	else
1255
		return null;
1256
}
1257

    
1258
function alias_expand_urltable($name) {
1259
	global $config;
1260
	$urltable_prefix = "/var/db/aliastables/";
1261
	$urltable_filename = $urltable_prefix . $name . ".txt";
1262

    
1263
	if (is_array($config['aliases']['alias'])) {
1264
		foreach ($config['aliases']['alias'] as $alias) {
1265
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1266
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1267
					return $urltable_filename;
1268
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1269
					return $urltable_filename;
1270
			}
1271
		}
1272
	}
1273
	return null;
1274
}
1275

    
1276
function subnet_size($subnet) {
1277
	if (is_subnetv4($subnet)) {
1278
		list ($ip, $bits) = explode("/", $subnet);
1279
		return round(exp(log(2) * (32 - $bits)));
1280
	}
1281
	else if (is_subnetv6($subnet)) {
1282
		list ($ip, $bits) = explode("/", $subnet);
1283
		return round(exp(log(2) * (128 - $bits)));
1284
	}
1285
	else {
1286
		return 0;
1287
	}
1288
}
1289

    
1290
function subnet_expand($subnet) {
1291
	if (is_subnetv4($subnet)) {
1292
		return subnetv4_expand($subnet);
1293
	} else if (is_subnetv6($subnet)) {
1294
		return subnetv6_expand($subnet);
1295
	} else {
1296
		return $subnet;
1297
	}
1298
}
1299

    
1300
function subnetv4_expand($subnet) {
1301
	$result = array();
1302
	list ($ip, $bits) = explode("/", $subnet);
1303
	$net  = ip2long($ip);
1304
	$mask = (0xffffffff << (32 - $bits));
1305
	$net &= $mask;
1306
	$size = round(exp(log(2) * (32 - $bits)));
1307
	for ($i = 0; $i < $size; $i += 1) {
1308
		$result[] = long2ip($net | $i);
1309
	}
1310
	return $result;
1311
}
1312

    
1313
/* find out whether two subnets overlap */
1314
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1315

    
1316
	if (!is_numeric($bits1))
1317
		$bits1 = 32;
1318
	if (!is_numeric($bits2))
1319
		$bits2 = 32;
1320

    
1321
	if ($bits1 < $bits2)
1322
		$relbits = $bits1;
1323
	else
1324
		$relbits = $bits2;
1325

    
1326
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1327
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1328

    
1329
	return ($sn1 == $sn2);
1330
}
1331

    
1332
/* find out whether two IPv6 subnets overlap */
1333
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
1334
	$sub1_min = gen_subnetv6($subnet1, $bits1);
1335
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
1336
	$sub2_min = gen_subnetv6($subnet2, $bits2);
1337
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
1338

    
1339
	return (is_inrange_v6($sub1_min, $sub2_min, $sub2_max) || is_inrange_v6($sub1_max, $sub2_min, $sub2_max) || is_inrange_v6($sub2_min, $sub1_min, $sub1_max));
1340
}
1341

    
1342
/* compare two IP addresses */
1343
function ipcmp($a, $b) {
1344
	if (ip_less_than($a, $b))
1345
		return -1;
1346
	else if (ip_greater_than($a, $b))
1347
		return 1;
1348
	else
1349
		return 0;
1350
}
1351

    
1352
/* return true if $addr is in $subnet, false if not */
1353
function ip_in_subnet($addr,$subnet) {
1354
	if(is_ipaddrv6($addr)) {
1355
		return (Net_IPv6::isInNetmask($addr, $subnet));
1356
	} else { /* XXX: Maybe check for IPv4 */
1357
		list($ip, $mask) = explode('/', $subnet);
1358
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1359
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1360
	}
1361
}
1362

    
1363
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1364
function verify_digital_signature($fname) {
1365
	global $g;
1366

    
1367
	if(!file_exists("/usr/local/sbin/gzsig"))
1368
		return 4;
1369

    
1370
	return mwexec("/usr/local/sbin/gzsig verify {$g['etc_path']}/pubkey.pem < " . escapeshellarg($fname));
1371
}
1372

    
1373
/* obtain MAC address given an IP address by looking at the ARP table */
1374
function arp_get_mac_by_ip($ip) {
1375
	mwexec("/sbin/ping -c 1 -t 1 {$ip}", true);
1376
	$arpoutput = "";
1377
	exec("/usr/sbin/arp -n {$ip}", $arpoutput);
1378

    
1379
	if ($arpoutput[0]) {
1380
		$arpi = explode(" ", $arpoutput[0]);
1381
		$macaddr = $arpi[3];
1382
		if (is_macaddr($macaddr))
1383
			return $macaddr;
1384
		else
1385
			return false;
1386
	}
1387

    
1388
	return false;
1389
}
1390

    
1391
/* return a fieldname that is safe for xml usage */
1392
function xml_safe_fieldname($fieldname) {
1393
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1394
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1395
			 ':', ',', '.', '\'', '\\'
1396
		);
1397
	return strtolower(str_replace($replace, "", $fieldname));
1398
}
1399

    
1400
function mac_format($clientmac) {
1401
	global $config, $cpzone;
1402

    
1403
	$mac = explode(":", $clientmac);
1404
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1405

    
1406
	switch($mac_format) {
1407
	case 'singledash':
1408
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1409

    
1410
	case 'ietf':
1411
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1412

    
1413
	case 'cisco':
1414
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1415

    
1416
	case 'unformatted':
1417
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1418

    
1419
	default:
1420
		return $clientmac;
1421
	}
1422
}
1423

    
1424
function resolve_retry($hostname, $retries = 5) {
1425

    
1426
	if (is_ipaddr($hostname))
1427
		return $hostname;
1428

    
1429
	for ($i = 0; $i < $retries; $i++) {
1430
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1431
		$ip = gethostbyname($hostname);
1432

    
1433
		if ($ip && $ip != $hostname) {
1434
			/* success */
1435
			return $ip;
1436
		}
1437

    
1438
		sleep(1);
1439
	}
1440

    
1441
	return false;
1442
}
1443

    
1444
function format_bytes($bytes) {
1445
	if ($bytes >= 1073741824) {
1446
		return sprintf("%.2f GB", $bytes/1073741824);
1447
	} else if ($bytes >= 1048576) {
1448
		return sprintf("%.2f MB", $bytes/1048576);
1449
	} else if ($bytes >= 1024) {
1450
		return sprintf("%.0f KB", $bytes/1024);
1451
	} else {
1452
		return sprintf("%d bytes", $bytes);
1453
	}
1454
}
1455

    
1456
function update_filter_reload_status($text) {
1457
	global $g;
1458

    
1459
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1460
}
1461

    
1462
/****** util/return_dir_as_array
1463
 * NAME
1464
 *   return_dir_as_array - Return a directory's contents as an array.
1465
 * INPUTS
1466
 *   $dir          - string containing the path to the desired directory.
1467
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1468
 * RESULT
1469
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1470
 ******/
1471
function return_dir_as_array($dir, $filter_regex = '') {
1472
	$dir_array = array();
1473
	if (is_dir($dir)) {
1474
		if ($dh = opendir($dir)) {
1475
			while (($file = readdir($dh)) !== false) {
1476
				if (($file == ".") || ($file == ".."))
1477
					continue;
1478

    
1479
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1480
					array_push($dir_array, $file);
1481
			}
1482
			closedir($dh);
1483
		}
1484
	}
1485
	return $dir_array;
1486
}
1487

    
1488
function run_plugins($directory) {
1489
	global $config, $g;
1490

    
1491
	/* process packager manager custom rules */
1492
	$files = return_dir_as_array($directory);
1493
	if (is_array($files)) {
1494
		foreach ($files as $file) {
1495
			if (stristr($file, ".sh") == true)
1496
				mwexec($directory . $file . " start");
1497
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc"))
1498
				require_once($directory . "/" . $file);
1499
		}
1500
	}
1501
}
1502

    
1503
/*
1504
 *    safe_mkdir($path, $mode = 0755)
1505
 *    create directory if it doesn't already exist and isn't a file!
1506
 */
1507
function safe_mkdir($path, $mode=0755) {
1508
	global $g;
1509

    
1510
	if (!is_file($path) && !is_dir($path)) {
1511
		return @mkdir($path, $mode, true);
1512
	} else {
1513
		return false;
1514
	}
1515
}
1516

    
1517
/*
1518
 * make_dirs($path, $mode = 0755)
1519
 * create directory tree recursively (mkdir -p)
1520
 */
1521
function make_dirs($path, $mode = 0755) {
1522
	$base = '';
1523
	foreach (explode('/', $path) as $dir) {
1524
		$base .= "/$dir";
1525
		if (!is_dir($base)) {
1526
			if (!@mkdir($base, $mode))
1527
				return false;
1528
		}
1529
	}
1530
	return true;
1531
}
1532

    
1533
/*
1534
 * get_sysctl($names)
1535
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1536
 * name) and return an array of key/value pairs set for those that exist
1537
 */
1538
function get_sysctl($names) {
1539
	if (empty($names))
1540
		return array();
1541

    
1542
	if (is_array($names)) {
1543
		$name_list = array();
1544
		foreach ($names as $name) {
1545
			$name_list[] = escapeshellarg($name);
1546
		}
1547
	} else
1548
		$name_list = array(escapeshellarg($names));
1549

    
1550
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1551
	$values = array();
1552
	foreach ($output as $line) {
1553
		$line = explode(": ", $line, 2);
1554
		if (count($line) == 2)
1555
			$values[$line[0]] = $line[1];
1556
	}
1557

    
1558
	return $values;
1559
}
1560

    
1561
/*
1562
 * set_sysctl($value_list)
1563
 * Set sysctl OID's listed as key/value pairs and return
1564
 * an array with keys set for those that succeeded
1565
 */
1566
function set_sysctl($values) {
1567
	if (empty($values))
1568
		return array();
1569

    
1570
	$value_list = array();
1571
	foreach ($values as $key => $value) {
1572
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1573
	}
1574

    
1575
	exec("/sbin/sysctl -i " . implode(" ", $value_list), $output, $success);
1576

    
1577
	/* Retry individually if failed (one or more read-only) */
1578
	if ($success <> 0 && count($value_list) > 1) {
1579
		foreach ($value_list as $value) {
1580
			exec("/sbin/sysctl -i " . $value, $output);
1581
		}
1582
	}
1583

    
1584
	$ret = array();
1585
	foreach ($output as $line) {
1586
		$line = explode(": ", $line, 2);
1587
		if (count($line) == 2)
1588
			$ret[$line[0]] = true;
1589
	}
1590

    
1591
	return $ret;
1592
}
1593

    
1594
/*
1595
 *     get_memory()
1596
 *     returns an array listing the amount of
1597
 *     memory installed in the hardware
1598
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1599
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1600
 */
1601
function get_memory() {
1602

    
1603
	$physmem = trim(`sysctl -n hw.physmem`, " \n");
1604
	$realmem = trim(`sysctl -n hw.realmem`, " \n");
1605
	/* convert from bytes to megabytes */
1606
	return array(($physmem/1048576),($realmem/1048576));
1607
}
1608

    
1609
function mute_kernel_msgs() {
1610
	global $config;
1611
	// Do not mute serial console.  The kernel gets very very cranky
1612
	// and will start dishing you cannot control tty errors.
1613
	switch (trim(file_get_contents("/etc/platform"))) {
1614
		case "nanobsd":
1615
		case "jail":
1616
			return;
1617
	}
1618
	if($config['system']['enableserial'])
1619
		return;
1620
	exec("/sbin/conscontrol mute on");
1621
}
1622

    
1623
function unmute_kernel_msgs() {
1624
	global $config;
1625
	// Do not mute serial console.  The kernel gets very very cranky
1626
	// and will start dishing you cannot control tty errors.
1627
	switch (trim(file_get_contents("/etc/platform"))) {
1628
		case "nanobsd":
1629
		case "jail":
1630
			return;
1631
	}
1632
	exec("/sbin/conscontrol mute off");
1633
}
1634

    
1635
function start_devd() {
1636
	global $g;
1637

    
1638
	if ($g['platform'] == 'jail')
1639
		return;
1640
	exec("/sbin/devd");
1641
	sleep(1);
1642
}
1643

    
1644
function is_interface_vlan_mismatch() {
1645
	global $config, $g;
1646

    
1647
	if (is_array($config['vlans']['vlan'])) {
1648
		foreach ($config['vlans']['vlan'] as $vlan) {
1649
			if (does_interface_exist($vlan['if']) == false)
1650
				return true;
1651
		}
1652
	}
1653

    
1654
	return false;
1655
}
1656

    
1657
function is_interface_mismatch() {
1658
	global $config, $g;
1659

    
1660
	$do_assign = false;
1661
	$i = 0;
1662
	$missing_interfaces = array();
1663
	if (is_array($config['interfaces'])) {
1664
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1665
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1666
				// Do not check these interfaces.
1667
				$i++;
1668
				continue;
1669
			}
1670
			else if (does_interface_exist($ifcfg['if']) == false) {
1671
				$missing_interfaces[] = $ifcfg['if'];
1672
				$do_assign = true;
1673
			} else
1674
				$i++;
1675
		}
1676
	}
1677

    
1678
	if ($g['minimum_nic_count'] > $i) {
1679
		$do_assign = true;
1680
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1681
		$do_assign = false;
1682

    
1683
	if (!empty($missing_interfaces) && $do_assign)
1684
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1685
	else
1686
		@unlink("{$g['tmp_path']}/missing_interfaces");
1687

    
1688
	return $do_assign;
1689
}
1690

    
1691
/* sync carp entries to other firewalls */
1692
function carp_sync_client() {
1693
	global $g;
1694
	send_event("filter sync");
1695
}
1696

    
1697
/****f* util/isAjax
1698
 * NAME
1699
 *   isAjax - reports if the request is driven from prototype
1700
 * INPUTS
1701
 *   none
1702
 * RESULT
1703
 *   true/false
1704
 ******/
1705
function isAjax() {
1706
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1707
}
1708

    
1709
/****f* util/timeout
1710
 * NAME
1711
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1712
 * INPUTS
1713
 *   optional, seconds to wait before timeout. Default 9 seconds.
1714
 * RESULT
1715
 *   returns 1 char of user input or null if no input.
1716
 ******/
1717
function timeout($timer = 9) {
1718
	while(!isset($key)) {
1719
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1720
		else { echo chr(8). "{$timer}"; }
1721
		`/bin/stty -icanon min 0 time 25`;
1722
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1723
		`/bin/stty icanon`;
1724
		if ($key == '')
1725
			unset($key);
1726
		$timer--;
1727
		if ($timer == 0)
1728
			break;
1729
	}
1730
	return $key;
1731
}
1732

    
1733
/****f* util/msort
1734
 * NAME
1735
 *   msort - sort array
1736
 * INPUTS
1737
 *   $array to be sorted, field to sort by, direction of sort
1738
 * RESULT
1739
 *   returns newly sorted array
1740
 ******/
1741
function msort($array, $id="id", $sort_ascending=true) {
1742
	$temp_array = array();
1743
	while(count($array)>0) {
1744
		$lowest_id = 0;
1745
		$index=0;
1746
		foreach ($array as $item) {
1747
			if (isset($item[$id])) {
1748
				if ($array[$lowest_id][$id]) {
1749
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1750
						$lowest_id = $index;
1751
					}
1752
				}
1753
			}
1754
			$index++;
1755
		}
1756
		$temp_array[] = $array[$lowest_id];
1757
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1758
	}
1759
	if ($sort_ascending) {
1760
		return $temp_array;
1761
	} else {
1762
		return array_reverse($temp_array);
1763
	}
1764
}
1765

    
1766
/****f* util/color
1767
 * NAME
1768
 *   color - outputs a color code to the ansi terminal if supported
1769
 * INPUTS
1770
 *   color code or color name
1771
 * RESULT
1772
 *   Outputs the ansi color sequence for the color specified.  Default resets terminal.
1773
 ******/
1774
function color($color = "0m") {
1775
	/*
1776
		Color codes available:
1777
		 0m reset; clears all colors and styles (to white on black)
1778
		 1m bold on (see below)
1779
		 3m italics on
1780
		 4m underline on
1781
		 7m inverse on; reverses foreground & background colors
1782
		 9m strikethrough on
1783
		 22m bold off (see below)
1784
		 23m italics off
1785
		 24m underline off
1786
		 27m inverse off
1787
		 29m strikethrough off
1788
		 30m set foreground color to black
1789
		 31m set foreground color to red
1790
		 32m set foreground color to green
1791
		 33m set foreground color to yellow
1792
		 34m set foreground color to blue
1793
		 35m set foreground color to magenta (purple)
1794
		 36m set foreground color to cyan
1795
		 37m set foreground color to white
1796
		 40m  set background color to black
1797
		 41m set background color to red
1798
		 42m set background color to green
1799
		 43m set background color to yellow
1800
		 44m set background color to blue
1801
		 45m set background color to magenta (purple)
1802
		 46m set background color to cyan
1803
		 47m set background color to white
1804
		 49m set background color to default (black)
1805
	*/
1806
	// Allow caching of TERM to
1807
	// speedup subequence requests.
1808
	global $TERM;
1809
	if(!$TERM)
1810
		$TERM=`/usr/bin/env | grep color`;
1811
	if(!$TERM)
1812
		$TERM=`/usr/bin/env | grep cons25`;
1813
	if($TERM) {
1814
		$ESCAPE=chr(27);
1815
		switch ($color) {
1816
			case "black":
1817
				return "{$ESCAPE}[30m";
1818
			case "red":
1819
				return "{$ESCAPE}[31m";
1820
			case "green":
1821
				return "{$ESCAPE}[32m";
1822
			case "yellow":
1823
				return "{$ESCAPE}[33m";
1824
			case "blue":
1825
				return "{$ESCAPE}[34m";
1826
			case "magenta":
1827
				return "{$ESCAPE}[35m";
1828
			case "cyan":
1829
				return "{$ESCAPE}[36m";
1830
			case "white":
1831
				return "{$ESCAPE}[37m";
1832
			case "default":
1833
				return "{$ESCAPE}[39m";
1834
		}
1835
		return "{$ESCAPE}[{$color}";
1836
	}
1837
}
1838

    
1839
/****f* util/is_URL
1840
 * NAME
1841
 *   is_URL
1842
 * INPUTS
1843
 *   string to check
1844
 * RESULT
1845
 *   Returns true if item is a URL
1846
 ******/
1847
function is_URL($url) {
1848
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1849
	if($match)
1850
		return true;
1851
	return false;
1852
}
1853

    
1854
function is_file_included($file = "") {
1855
	$files = get_included_files();
1856
	if (in_array($file, $files))
1857
		return true;
1858

    
1859
	return false;
1860
}
1861

    
1862
/*
1863
 * Replace a value on a deep associative array using regex
1864
 */
1865
function array_replace_values_recursive($data, $match, $replace) {
1866
	if (empty($data))
1867
		return $data;
1868

    
1869
	if (is_string($data))
1870
		$data = preg_replace("/{$match}/", $replace, $data);
1871
	else if (is_array($data))
1872
		foreach ($data as $k => $v)
1873
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1874

    
1875
	return $data;
1876
}
1877

    
1878
/*
1879
	This function was borrowed from a comment on PHP.net at the following URL:
1880
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1881
 */
1882
function array_merge_recursive_unique($array0, $array1) {
1883

    
1884
	$arrays = func_get_args();
1885
	$remains = $arrays;
1886

    
1887
	// We walk through each arrays and put value in the results (without
1888
	// considering previous value).
1889
	$result = array();
1890

    
1891
	// loop available array
1892
	foreach($arrays as $array) {
1893

    
1894
		// The first remaining array is $array. We are processing it. So
1895
		// we remove it from remaing arrays.
1896
		array_shift($remains);
1897

    
1898
		// We don't care non array param, like array_merge since PHP 5.0.
1899
		if(is_array($array)) {
1900
			// Loop values
1901
			foreach($array as $key => $value) {
1902
				if(is_array($value)) {
1903
					// we gather all remaining arrays that have such key available
1904
					$args = array();
1905
					foreach($remains as $remain) {
1906
						if(array_key_exists($key, $remain)) {
1907
							array_push($args, $remain[$key]);
1908
						}
1909
					}
1910

    
1911
					if(count($args) > 2) {
1912
						// put the recursion
1913
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1914
					} else {
1915
						foreach($value as $vkey => $vval) {
1916
							$result[$key][$vkey] = $vval;
1917
						}
1918
					}
1919
				} else {
1920
					// simply put the value
1921
					$result[$key] = $value;
1922
				}
1923
			}
1924
		}
1925
	}
1926
	return $result;
1927
}
1928

    
1929

    
1930
/*
1931
 * converts a string like "a,b,c,d"
1932
 * into an array like array("a" => "b", "c" => "d")
1933
 */
1934
function explode_assoc($delimiter, $string) {
1935
	$array = explode($delimiter, $string);
1936
	$result = array();
1937
	$numkeys = floor(count($array) / 2);
1938
	for ($i = 0; $i < $numkeys; $i += 1) {
1939
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1940
	}
1941
	return $result;
1942
}
1943

    
1944
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
1945
	global $config, $aliastable;
1946

    
1947
	/* Bail if there are no routes, but return an array always so callers don't have to check. */
1948
	if (!is_array($config['staticroutes']['route']))
1949
		return array();
1950

    
1951
	$allstaticroutes = array();
1952
	$allsubnets = array();
1953
	/* Loop through routes and expand aliases as we find them. */
1954
	foreach ($config['staticroutes']['route'] as $route) {
1955
		if (is_alias($route['network'])) {
1956
			if (!isset($aliastable[$route['network']]))
1957
				continue;
1958

    
1959
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
1960
			foreach ($subnets as $net) {
1961
				if (!is_subnet($net)) {
1962
					if (is_ipaddrv4($net))
1963
						$net .= "/32";
1964
					else if (is_ipaddrv6($net))
1965
						$net .= "/128";
1966
					else if ($returnhostnames === false || !is_fqdn($net))
1967
						continue;
1968
				}
1969
				$temproute = $route;
1970
				$temproute['network'] = $net;
1971
				$allstaticroutes[] = $temproute;
1972
				$allsubnets[] = $net;
1973
			}
1974
		} elseif (is_subnet($route['network'])) {
1975
			$allstaticroutes[] = $route;
1976
			$allsubnets[] = $route['network'];
1977
		}
1978
	}
1979
	if ($returnsubnetsonly)
1980
		return $allsubnets;
1981
	else
1982
		return $allstaticroutes;
1983
}
1984

    
1985
/****f* util/get_alias_list
1986
 * NAME
1987
 *   get_alias_list - Provide a list of aliases.
1988
 * INPUTS
1989
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
1990
 * RESULT
1991
 *   Array containing list of aliases.
1992
 *   If $type is unspecified, all aliases are returned.
1993
 *   If $type is a string, all aliases of the type specified in $type are returned.
1994
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
1995
 */
1996
function get_alias_list($type = null) {
1997
	global $config;
1998
	$result = array();
1999
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
2000
		foreach ($config['aliases']['alias'] as $alias) {
2001
			if ($type === null) {
2002
				$result[] = $alias['name'];
2003
			}
2004
			else if (is_array($type)) {
2005
				if (in_array($alias['type'], $type)) {
2006
					$result[] = $alias['name'];
2007
				}
2008
			}
2009
			else if ($type === $alias['type']) {
2010
				$result[] = $alias['name'];
2011
			}
2012
		}
2013
	}
2014
	return $result;
2015
}
2016

    
2017
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
2018
function array_exclude($needle, $haystack) {
2019
	$result = array();
2020
	if (is_array($haystack)) {
2021
		foreach ($haystack as $thing) {
2022
			if ($needle !== $thing) {
2023
				$result[] = $thing;
2024
			}
2025
		}
2026
	}
2027
	return $result;
2028
}
2029

    
2030
function setup_library_paths() {
2031
	$current_library_paths = explode(":", exec("/sbin/ldconfig -r | /usr/bin/grep 'search directories' | /usr/bin/awk '{print $3;}'"));
2032
	$pbi_library_paths = array_merge(glob("/usr/pbi/*/lib", GLOB_ONLYDIR), glob("/usr/pbi/*/lib/*", GLOB_ONLYDIR));
2033
	foreach ($pbi_library_paths as $pbilib) {
2034
		if (!in_array($pbilib, $current_library_paths))
2035
			exec("/sbin/ldconfig -m {$pbilib}");
2036
	}
2037
}
2038

    
2039
function get_current_theme() {
2040
	global $config, $g;
2041
	/*
2042
	 *   if user has selected a custom template, use it.
2043
	 *   otherwise default to pfsense tempalte
2044
	 */
2045
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
2046
		$theme = $g["default_theme"];
2047
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
2048
		$theme = $config['theme'];
2049
	else
2050
		$theme = "pfsense";
2051
	/*
2052
	 *  If this device is an apple ipod/iphone
2053
	 *  switch the theme to one that works with it.
2054
	 */
2055
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2056
	foreach($lowres_ua as $useragent)
2057
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
2058
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2059
	return $theme;
2060
}
2061

    
2062
?>
(54-54/66)