Project

General

Profile

Download (51.8 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
/* unlock configuration file */
136
function unlock($cfglckkey = 0) {
137
	global $g, $cfglckkeyconsumers;
138
	flock($cfglckkey, LOCK_UN);
139
	fclose($cfglckkey);
140
	return;
141
}
142

    
143
function send_event($cmd) {
144
	global $g;
145

    
146
	if(!isset($g['event_address']))
147
		$g['event_address'] = "unix:///var/run/check_reload_status";
148

    
149
	$try = 0;
150
	while ($try < 3) {
151
		$fd = @fsockopen($g['event_address']);
152
		if ($fd) {
153
			fwrite($fd, $cmd);
154
			$resp = fread($fd, 4096);
155
			if ($resp != "OK\n")
156
				log_error("send_event: sent {$cmd} got {$resp}");
157
			fclose($fd);
158
			$try = 3;
159
		} else if (!is_process_running("check_reload_status"))
160
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
161
		$try++;
162
	}
163
}
164

    
165
function send_multiple_events($cmds) {
166
	global $g;
167

    
168
	if(!isset($g['event_address']))
169
		$g['event_address'] = "unix:///var/run/check_reload_status";
170

    
171
	if (!is_array($cmds))
172
		return;
173

    
174
	while ($try < 3) {
175
		$fd = @fsockopen($g['event_address']);
176
		if ($fd) {
177
			foreach ($cmds as $cmd) {
178
				fwrite($fd, $cmd);
179
				$resp = fread($fd, 4096);
180
				if ($resp != "OK\n")
181
					log_error("send_event: sent {$cmd} got {$resp}");
182
			}
183
			fclose($fd);
184
			$try = 3;
185
		} else if (!is_process_running("check_reload_status"))
186
			mwexec_bg("/usr/bin/nice -n20 /usr/local/sbin/check_reload_status");
187
		$try++;
188
	}
189
}
190

    
191
function refcount_init($reference) {
192
	$shmid = @shmop_open($reference, "c", 0644, 10);
193
	@shmop_write($shmid, str_pad("0", 10, "\x0", STR_PAD_RIGHT), 0);
194
	@shmop_close($shmid);
195
}
196

    
197
function refcount_reference($reference) {
198
	/* Take out a lock across the shared memory read, increment, write sequence to make it atomic. */
199
	$shm_lck = lock("shm{$reference}", LOCK_EX);
200
	try {
201
		/* NOTE: A warning is generated when shared memory does not exist */
202
		$shmid = @shmop_open($reference, "w", 0, 0);
203
		if (!$shmid) {
204
			refcount_init($reference);
205
			$shmid = @shmop_open($reference, "w", 0, 0);
206
			if (!$shmid) {
207
				log_error(gettext("Could not open shared memory {$reference}"));
208
				unlock($shm_lck);
209
				return;
210
			}
211
		}
212
		$shm_data = @shmop_read($shmid, 0, 10);
213
		$shm_data = intval($shm_data) + 1;
214
		@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
215
		@shmop_close($shmid);
216
		unlock($shm_lck);
217
	} catch (Exception $e) {
218
		log_error($e->getMessage());
219
		unlock($shm_lck);
220
	}
221

    
222
	return $shm_data;
223
}
224

    
225
function refcount_unreference($reference) {
226
	/* Take out a lock across the shared memory read, decrement, write sequence to make it atomic. */
227
	$shm_lck = lock("shm{$reference}", LOCK_EX);
228
	try {
229
		$shmid = @shmop_open($reference, "w", 0, 0);
230
		if (!$shmid) {
231
			refcount_init($reference);
232
			log_error(gettext("Could not open shared memory {$reference}"));
233
			unlock($shm_lck);
234
			return;
235
		}
236
		$shm_data = @shmop_read($shmid, 0, 10);
237
		$shm_data = intval($shm_data) - 1;
238
		if ($shm_data < 0) {
239
			//debug_backtrace();
240
			log_error(sprintf(gettext("Reference %s is going negative, not doing unreference."), $reference));
241
		} else
242
			@shmop_write($shmid, str_pad($shm_data, 10, "\x0", STR_PAD_RIGHT), 0);
243
		@shmop_close($shmid);
244
		unlock($shm_lck);
245
	} catch (Exception $e) {
246
		log_error($e->getMessage());
247
		unlock($shm_lck);
248
	}
249

    
250
	return $shm_data;
251
}
252

    
253
function refcount_read($reference) {
254
	/* This function just reads the current value of the refcount for information. */
255
	/* There is no need for locking. */
256
	$shmid = @shmop_open($reference, "a", 0, 0);
257
	if (!$shmid) {
258
		log_error(gettext("Could not open shared memory for read {$reference}"));
259
		return -1;
260
	}
261
	$shm_data = @shmop_read($shmid, 0, 10);
262
	@shmop_close($shmid);
263
	return $shm_data;
264
}
265

    
266
function is_module_loaded($module_name) {
267
	$running = `/sbin/kldstat | grep {$module_name} | /usr/bin/grep -v grep | /usr/bin/wc -l`;
268
	if (intval($running) >= 1)
269
		return true;
270
	else
271
		return false;
272
}
273

    
274
/* return the subnet address given a host address and a subnet bit count */
275
function gen_subnet($ipaddr, $bits) {
276
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
277
		return "";
278
	return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits));
279
}
280

    
281
/* return the subnet address given a host address and a subnet bit count */
282
function gen_subnetv6($ipaddr, $bits) {
283
	if (!is_ipaddrv6($ipaddr) || !is_numeric($bits))
284
		return "";
285

    
286
	$address = Net_IPv6::getNetmask($ipaddr, $bits);
287
	$address = Net_IPv6::compress($address);
288
	return $address;
289
}
290

    
291
/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */
292
function gen_subnet_max($ipaddr, $bits) {
293
	if (!is_ipaddr($ipaddr) || !is_numeric($bits))
294
		return "";
295

    
296
	return long2ip32(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
297
}
298

    
299
/* Generate end number for a given ipv6 subnet mask */
300
function gen_subnetv6_max($ipaddr, $bits) {
301
	if(!is_ipaddrv6($ipaddr))
302
		return false;
303

    
304
	$mask = Net_IPv6::getNetmask('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',$bits);
305

    
306
	$inet_ip = (binary)inet_pton($ipaddr);
307
	$inet_mask = (binary)inet_pton($mask);
308

    
309
	$inet_end = $inet_ip | ~$inet_mask;
310

    
311
	return (inet_ntop($inet_end));
312
}
313

    
314
/* returns a subnet mask (long given a bit count) */
315
function gen_subnet_mask_long($bits) {
316
	$sm = 0;
317
	for ($i = 0; $i < $bits; $i++) {
318
		$sm >>= 1;
319
		$sm |= 0x80000000;
320
	}
321
	return $sm;
322
}
323

    
324
/* same as above but returns a string */
325
function gen_subnet_mask($bits) {
326
	return long2ip(gen_subnet_mask_long($bits));
327
}
328

    
329
/* Convert long int to IP address, truncating to 32-bits. */
330
function long2ip32($ip) {
331
	return long2ip($ip & 0xFFFFFFFF);
332
}
333

    
334
/* Convert IP address to long int, truncated to 32-bits to avoid sign extension on 64-bit platforms. */
335
function ip2long32($ip) {
336
	return ( ip2long($ip) & 0xFFFFFFFF );
337
}
338

    
339
/* Convert IP address to unsigned long int. */
340
function ip2ulong($ip) {
341
	return sprintf("%u", ip2long32($ip));
342
}
343

    
344
/* Find out how many IPs are contained within a given IP range
345
 *  e.g. 192.168.0.0 to 192.168.0.255 returns 256
346
 */
347
function ip_range_size($startip, $endip) {
348
	if (is_ipaddr($startip) && is_ipaddr($endip)) {
349
		// Operate as unsigned long because otherwise it wouldn't work
350
		//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
351
		return abs(ip2ulong($startip) - ip2ulong($endip)) + 1;
352
	}
353
	return -1;
354
}
355

    
356
/* Find the smallest possible subnet mask which can contain a given number of IPs
357
 *  e.g. 512 IPs can fit in a /23, but 513 IPs need a /22
358
 */
359
function find_smallest_cidr($number) {
360
	$smallest = 1;
361
	for ($b=32; $b > 0; $b--) {
362
		$smallest = ($number <= pow(2,$b)) ? $b : $smallest;
363
	}
364
	return (32-$smallest);
365
}
366

    
367
/* Return the previous IP address before the given address */
368
function ip_before($ip) {
369
	return long2ip32(ip2long($ip)-1);
370
}
371

    
372
/* Return the next IP address after the given address */
373
function ip_after($ip) {
374
	return long2ip32(ip2long($ip)+1);
375
}
376

    
377
/* Return true if the first IP is 'before' the second */
378
function ip_less_than($ip1, $ip2) {
379
	// Compare as unsigned long because otherwise it wouldn't work when
380
	//   crossing over from 127.255.255.255 / 128.0.0.0 barrier
381
	return ip2ulong($ip1) < ip2ulong($ip2);
382
}
383

    
384
/* Return true if the first IP is 'after' the second */
385
function ip_greater_than($ip1, $ip2) {
386
	// Compare as unsigned long because otherwise it wouldn't work
387
	//   when crossing over from 127.255.255.255 / 128.0.0.0 barrier
388
	return ip2ulong($ip1) > ip2ulong($ip2);
389
}
390

    
391
/* Convert a range of IPs to an array of subnets which can contain the range. */
392
function ip_range_to_subnet_array($startip, $endip) {
393
	if (!is_ipaddr($startip) || !is_ipaddr($endip)) {
394
		return array();
395
	}
396

    
397
	// Container for subnets within this range.
398
	$rangesubnets = array();
399

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

    
403
	// Loop here to reduce subnet size and retest as needed. We need to make sure
404
	//   that the target subnet is wholly contained between $startip and $endip.
405
	for ($cidr; $cidr <= 32; $cidr++) {
406
		// Find the network and broadcast addresses for the subnet being tested.
407
		$targetsub_min = gen_subnet($startip, $cidr);
408
		$targetsub_max = gen_subnet_max($startip, $cidr);
409

    
410
		// Check best case where the range is exactly one subnet.
411
		if (($targetsub_min == $startip) && ($targetsub_max == $endip)) {
412
			// Hooray, the range is exactly this subnet!
413
			return array("{$startip}/{$cidr}");
414
		}
415

    
416
		// These remaining scenarios will find a subnet that uses the largest
417
		//  chunk possible of the range being tested, and leave the rest to be
418
		//  tested recursively after the loop.
419

    
420
		// Check if the subnet begins with $startip and ends before $endip
421
		if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) {
422
			break;
423
		}
424

    
425
		// Check if the subnet ends at $endip and starts after $startip
426
		if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) {
427
			break;
428
		}
429

    
430
		// Check if the subnet is between $startip and $endip
431
		if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) {
432
			break;
433
		}
434
	}
435

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

    
442
	// Add in the subnet we found before, to preserve ordering
443
	$rangesubnets[] = "{$targetsub_min}/{$cidr}";
444

    
445
	// And some more logic that will search after the subnet we found to fill in to the end of the range.
446
	if ($endip != $targetsub_max) {
447
		$rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip));
448
	}
449
	return $rangesubnets;
450
}
451

    
452
function is_iprange($range) {
453
	if (substr_count($range, '-') != 1) {
454
		return false;
455
	}
456
	list($ip1, $ip2) = explode ('-', $range);
457
	return (is_ipaddr($ip1) && is_ipaddr($ip2));
458
}
459

    
460
function is_numericint($arg) {
461
	return (preg_match("/[^0-9]/", $arg) ? false : true);
462
}
463

    
464

    
465
/* returns true if $ipaddr is a valid dotted IPv4 address or a IPv6 */
466
function is_ipaddr($ipaddr) {
467
	if(is_ipaddrv4($ipaddr)) {
468
		return true;
469
	}
470
	if(is_ipaddrv6($ipaddr)) {
471
		return true;
472
	}
473
	return false;
474
}
475

    
476
/* returns true if $ipaddr is a valid IPv6 address */
477
function is_ipaddrv6($ipaddr) {
478
	if (!is_string($ipaddr) || empty($ipaddr))
479
		return false;
480
	if (strstr($ipaddr, "%") && is_linklocal($ipaddr)) {
481
		$tmpip = explode("%", $ipaddr);
482
		$ipaddr = $tmpip[0];
483
	}
484
	return Net_IPv6::checkIPv6($ipaddr);
485
}
486

    
487
/* returns true if $ipaddr is a valid dotted IPv4 address */
488
function is_ipaddrv4($ipaddr) {
489
	if (!is_string($ipaddr) || empty($ipaddr))
490
		return false;
491

    
492
	$ip_long = ip2long($ipaddr);
493
	$ip_reverse = long2ip32($ip_long);
494

    
495
	if ($ipaddr == $ip_reverse)
496
		return true;
497
	else
498
		return false;
499
}
500

    
501
/* returns true if $ipaddr is a valid linklocal address */
502
function is_linklocal($ipaddr) {
503
	return (substr($ipaddr, 0, 5) == "fe80:");
504
}
505

    
506
/* returns scope of a linklocal address */
507
function get_ll_scope($addr) {
508
	if (!is_linklocal($addr) || !strstr($addr, "%"))
509
		return "";
510
	list ($ll, $scope) = explode("%", $addr);
511
	return $scope;
512
}
513

    
514
/* returns true if $ipaddr is a valid literal IPv6 address */
515
function is_literalipaddrv6($ipaddr) {
516
	if(preg_match("/\[([0-9a-f:]+)\]/i", $ipaddr, $match))
517
		$ipaddr = $match[1];
518
	else
519
		return false;
520

    
521
	return is_ipaddrv6($ipaddr);
522
}
523

    
524
function is_ipaddrwithport($ipport) {
525
	$parts = explode(":", $ipport);
526
	$port = array_pop($parts);
527
	if (count($parts) == 1) {
528
		return is_ipaddrv4($parts[0]) && is_port($port);
529
	} elseif (count($parts) > 1) {
530
		return is_literalipaddrv6(implode(":", $parts)) && is_port($port);
531
	} else {
532
		return false;
533
	}
534
}
535

    
536
function is_hostnamewithport($hostport) {
537
	$parts = explode(":", $hostport);
538
	$port = array_pop($parts);
539
	if (count($parts) == 1) {
540
		return is_hostname($parts[0]) && is_port($port);
541
	} else {
542
		return false;
543
	}
544
}
545

    
546
/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
547
function is_ipaddroralias($ipaddr) {
548
	global $config;
549

    
550
	if (is_alias($ipaddr)) {
551
		if (is_array($config['aliases']['alias'])) {
552
			foreach ($config['aliases']['alias'] as $alias) {
553
				if ($alias['name'] == $ipaddr && !preg_match("/port/i", $alias['type']))
554
					return true;
555
			}
556
		}
557
		return false;
558
	} else
559
		return is_ipaddr($ipaddr);
560

    
561
}
562

    
563
/* returns true if $subnet is a valid IPv4 or IPv6 subnet in CIDR format */
564
function is_subnet($subnet) {
565
	if(is_subnetv4($subnet)) {
566
		return true;
567
	}
568
	if(is_subnetv6($subnet)) {
569
		return true;
570
	}
571
	return false;
572
}
573

    
574
/* returns true if $subnet is a valid IPv4 subnet in CIDR format */
575
function is_subnetv4($subnet) {
576
	if (!is_string($subnet))
577
		return false;
578

    
579
	list($hp,$np) = explode('/', $subnet);
580

    
581
	if (!is_ipaddrv4($hp))
582
		return false;
583

    
584
	if (!is_numeric($np) || ($np < 1) || ($np > 32))
585
		return false;
586

    
587
	return true;
588
}
589

    
590
/* returns true if $subnet is a valid IPv6 subnet in CIDR format */
591
function is_subnetv6($subnet) {
592
	if (!is_string($subnet))
593
		return false;
594

    
595
	list($hp,$np) = explode('/', $subnet);
596

    
597
	if (!is_ipaddrv6($hp))
598
		return false;
599

    
600
	if (!is_numeric($np) || ($np < 1) || ($np > 128))
601
		return false;
602

    
603
	return true;
604
}
605

    
606

    
607
/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
608
function is_subnetoralias($subnet) {
609
	global $aliastable;
610

    
611
	if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
612
		return true;
613
	else
614
		return is_subnet($subnet);
615
}
616

    
617
/* returns true if $hostname is a valid hostname */
618
function is_hostname($hostname) {
619
	if (!is_string($hostname))
620
		return false;
621

    
622
	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))
623
		return true;
624
	else
625
		return false;
626
}
627

    
628
/* returns true if $domain is a valid domain name */
629
function is_domain($domain) {
630
	if (!is_string($domain))
631
		return false;
632

    
633
	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))
634
		return true;
635
	else
636
		return false;
637
}
638

    
639
/* returns true if $macaddr is a valid MAC address */
640
function is_macaddr($macaddr, $partial=false) {
641
	$repeat = ($partial) ? '1,5' : '5';
642
	return preg_match('/^[0-9A-F]{2}(?:[:][0-9A-F]{2}){'.$repeat.'}$/i', $macaddr) == 1 ? true : false;
643
}
644

    
645
/* returns true if $name is a valid name for an alias */
646
/* returns NULL if a reserved word is used */
647
function is_validaliasname($name) {
648
	/* Array of reserved words */
649
	$reserved = array("port", "pass");
650
	if (in_array($name, $reserved, true))
651
		return; /* return NULL */
652
	if (!preg_match("/[^a-zA-Z0-9_]/", $name) && (strlen($name) < 32))
653
		return true;
654
	else
655
		return false;
656
}
657

    
658
/* returns true if $port is a valid TCP/UDP port */
659
function is_port($port) {
660
	$tmpports = explode(":", $port);
661
	foreach($tmpports as $tmpport) {
662
		if (getservbyname($tmpport, "tcp") || getservbyname($tmpport, "udp"))
663
			continue;
664
		if (!ctype_digit($tmpport))
665
			return false;
666
		else if ((intval($tmpport) < 1) || (intval($tmpport) > 65535))
667
			return false;
668
	}
669
	return true;
670
}
671

    
672
/* returns true if $portrange is a valid TCP/UDP portrange ("<port>:<port>") */
673
function is_portrange($portrange) {
674
	$ports = explode(":", $portrange);
675

    
676
	return (count($ports) == 2 && is_port($ports[0]) && is_port($ports[1]));
677
}
678

    
679
/* returns true if $port is a valid port number or an alias thereof */
680
function is_portoralias($port) {
681
	global $config;
682

    
683
	if (is_alias($port)) {
684
		if (is_array($config['aliases']['alias'])) {
685
			foreach ($config['aliases']['alias'] as $alias) {
686
				if ($alias['name'] == $port && preg_match("/port/i", $alias['type']))
687
					return true;
688
				}
689
			}
690
			return false;
691
	} else
692
		return is_port($port);
693
}
694

    
695
/* create ranges of sequential port numbers (200:215) and remove duplicates */
696
function group_ports($ports) {
697
	if (!is_array($ports) || empty($ports))
698
		return;
699

    
700
	$uniq = array();
701
	foreach ($ports as $port) {
702
		if (is_portrange($port)) {
703
			list($begin, $end) = explode(":", $port);
704
			if ($begin > $end) {
705
				$aux = $begin;
706
				$begin = $end;
707
				$end = $aux;
708
			}
709
			for ($i = $begin; $i <= $end; $i++)
710
				if (!in_array($i, $uniq))
711
					$uniq[] = $i;
712
		} else if (is_port($port)) {
713
			if (!in_array($port, $uniq))
714
				$uniq[] = $port;
715
		}
716
	}
717
	sort($uniq, SORT_NUMERIC);
718

    
719
	$result = array();
720
	foreach ($uniq as $idx => $port) {
721
		if ($idx == 0) {
722
			$result[] = $port;
723
			continue;
724
		}
725

    
726
		$last = end($result);
727
		if (is_portrange($last))
728
			list($begin, $end) = explode(":", $last);
729
		else
730
			$begin = $end = $last;
731

    
732
		if ($port == ($end+1)) {
733
			$end++;
734
			$result[count($result)-1] = "{$begin}:{$end}";
735
		} else {
736
			$result[] = $port;
737
		}
738
	}
739

    
740
	return $result;
741
}
742

    
743
/* returns true if $val is a valid shaper bandwidth value */
744
function is_valid_shaperbw($val) {
745
	return (preg_match("/^(\d+(?:\.\d+)?)([MKG]?b|%)$/", $val));
746
}
747

    
748
/* returns true if $test is in the range between $start and $end */
749
function is_inrange_v4($test, $start, $end) {
750
	if ( (ip2ulong($test) <= ip2ulong($end)) && (ip2ulong($test) >= ip2ulong($start)) )
751
		return true;
752
	else
753
		return false;
754
}
755

    
756
/* returns true if $test is in the range between $start and $end */
757
function is_inrange_v6($test, $start, $end) {
758
	if ( (inet_pton($test) <= inet_pton($end)) && (inet_pton($test) >= inet_pton($start)) )
759
		return true;
760
	else
761
		return false;
762
}
763

    
764
/* return the configured carp interface list */
765
function get_configured_carp_interface_list() {
766
	global $config;
767

    
768
	$iflist = array();
769

    
770
	if(is_array($config['virtualip']['vip'])) {
771
		$viparr = &$config['virtualip']['vip'];
772
		foreach ($viparr as $vip) {
773
			switch ($vip['mode']) {
774
			case "carp":
775
				$vipif = "{$vip['interface']}_vip{$vip['vhid']}";
776
				$iflist[$vipif] = $vip['subnet'];
777
				break;
778
			}
779
		}
780
	}
781

    
782
	return $iflist;
783
}
784

    
785
/* return the configured IP aliases list */
786
function get_configured_ip_aliases_list($returnfullentry = false) {
787
	global $config;
788

    
789
	$alias_list=array();
790

    
791
	if(is_array($config['virtualip']['vip'])) {
792
		$viparr = &$config['virtualip']['vip'];
793
		foreach ($viparr as $vip) {
794
			if ($vip['mode']=="ipalias") {
795
				if ($returnfullentry)
796
					$alias_list[$vip['subnet']] = $vip;
797
				else
798
					$alias_list[$vip['subnet']] = $vip['interface'];
799
			}
800
		}
801
	}
802

    
803
	return $alias_list;
804
}
805

    
806
/* return all configured aliases list (IP, carp, proxyarp and other) */
807
function get_configured_vips_list() {
808
	global $config;
809

    
810
	$alias_list=array();
811

    
812
	if(is_array($config['virtualip']['vip'])) {
813
		$viparr = &$config['virtualip']['vip'];
814
		foreach ($viparr as $vip)
815
			$alias_list[] = array("ipaddr" => $vip['subnet'], "if" => $vip['interface']);
816
	}
817

    
818
	return $alias_list;
819
}
820

    
821
/* comparison function for sorting by the order in which interfaces are normally created */
822
function compare_interface_friendly_names($a, $b) {
823
	if ($a == $b)
824
		return 0;
825
	else if ($a == 'wan')
826
		return -1;
827
	else if ($b == 'wan')
828
		return 1;
829
	else if ($a == 'lan')
830
		return -1;
831
	else if ($b == 'lan')
832
		return 1;
833

    
834
	return strnatcmp($a, $b);
835
}
836

    
837
/* return the configured interfaces list. */
838
function get_configured_interface_list($only_opt = false, $withdisabled = false) {
839
	global $config;
840

    
841
	$iflist = array();
842

    
843
	/* if list */
844
	foreach($config['interfaces'] as $if => $ifdetail) {
845
		if ($only_opt && ($if == "wan" || $if == "lan"))
846
			continue;
847
		if (isset($ifdetail['enable']) || $withdisabled == true)
848
			$iflist[$if] = $if;
849
	}
850

    
851
	return $iflist;
852
}
853

    
854
/* return the configured interfaces list. */
855
function get_configured_interface_list_by_realif($only_opt = false, $withdisabled = false) {
856
	global $config;
857

    
858
	$iflist = array();
859

    
860
	/* if list */
861
	foreach($config['interfaces'] as $if => $ifdetail) {
862
		if ($only_opt && ($if == "wan" || $if == "lan"))
863
			continue;
864
		if (isset($ifdetail['enable']) || $withdisabled == true) {
865
			$tmpif = get_real_interface($if);
866
			if (!empty($tmpif))
867
				$iflist[$tmpif] = $if;
868
		}
869
	}
870

    
871
	return $iflist;
872
}
873

    
874
/* return the configured interfaces list with their description. */
875
function get_configured_interface_with_descr($only_opt = false, $withdisabled = false) {
876
	global $config;
877

    
878
	$iflist = array();
879

    
880
	/* if list */
881
	foreach($config['interfaces'] as $if => $ifdetail) {
882
		if ($only_opt && ($if == "wan" || $if == "lan"))
883
			continue;
884
		if (isset($ifdetail['enable']) || $withdisabled == true) {
885
			if(empty($ifdetail['descr']))
886
				$iflist[$if] = strtoupper($if);
887
			else
888
				$iflist[$if] = strtoupper($ifdetail['descr']);
889
		}
890
	}
891

    
892
	return $iflist;
893
}
894

    
895
/*
896
 *   get_configured_ip_addresses() - Return a list of all configured
897
 *   interfaces IP Addresses
898
 *
899
 */
900
function get_configured_ip_addresses() {
901
	global $config;
902

    
903
	if (!function_exists('get_interface_ip'))
904
		require_once("interfaces.inc");
905
	$ip_array = array();
906
	$interfaces = get_configured_interface_list();
907
	if (is_array($interfaces)) {
908
		foreach($interfaces as $int) {
909
			$ipaddr = get_interface_ip($int);
910
			$ip_array[$int] = $ipaddr;
911
		}
912
	}
913
	$interfaces = get_configured_carp_interface_list();
914
	if (is_array($interfaces))
915
		foreach($interfaces as $int => $ipaddr)
916
			$ip_array[$int] = $ipaddr;
917

    
918
	/* pppoe server */
919
	if (is_array($config['pppoes']) && is_array($config['pppoes']['pppoe'])) {
920
		foreach($config['pppoes']['pppoe'] as $pppoe) {
921
			if ($pppoe['mode'] == "server") {
922
				if(is_ipaddr($pppoe['localip'])) {
923
					$int = "pppoes". $pppoe['pppoeid'];
924
					$ip_array[$int] = $pppoe['localip'];
925
				}
926
			}
927
		}
928
	}
929

    
930
	return $ip_array;
931
}
932

    
933
/*
934
 *   get_configured_ipv6_addresses() - Return a list of all configured
935
 *   interfaces IPv6 Addresses
936
 *
937
 */
938
function get_configured_ipv6_addresses() {
939
	require_once("interfaces.inc");
940
	$ipv6_array = array();
941
	$interfaces = get_configured_interface_list();
942
	if(is_array($interfaces)) {
943
		foreach($interfaces as $int) {
944
			$ipaddrv6 = get_interface_ipv6($int);
945
			$ipv6_array[$int] = $ipaddrv6;
946
		}
947
	}
948
	$interfaces = get_configured_carp_interface_list();
949
	if(is_array($interfaces))
950
		foreach($interfaces as $int => $ipaddrv6)
951
			$ipv6_array[$int] = $ipaddrv6;
952
	return $ipv6_array;
953
}
954

    
955
/*
956
 *   get_interface_list() - Return a list of all physical interfaces
957
 *   along with MAC and status.
958
 *
959
 *   $mode = "active" - use ifconfig -lu
960
 *           "media"  - use ifconfig to check physical connection
961
 *			status (much slower)
962
 */
963
function get_interface_list($mode = "active", $keyby = "physical", $vfaces = "") {
964
	global $config;
965
	$upints = array();
966
	/* get a list of virtual interface types */
967
	if(!$vfaces) {
968
		$vfaces = array (
969
				'bridge',
970
				'ppp',
971
				'pppoe',
972
				'pptp',
973
				'l2tp',
974
				'sl',
975
				'gif',
976
				'gre',
977
				'faith',
978
				'lo',
979
				'ng',
980
				'_vlan',
981
				'_wlan',
982
				'pflog',
983
				'plip',
984
				'pfsync',
985
				'enc',
986
				'tun',
987
				'carp',
988
				'lagg',
989
				'vip',
990
				'ipfw'
991
		);
992
	}
993
	switch($mode) {
994
	case "active":
995
		$upints = pfSense_interface_listget(IFF_UP);
996
		break;
997
	case "media":
998
		$intlist = pfSense_interface_listget();
999
		$ifconfig = "";
1000
		exec("/sbin/ifconfig -a", $ifconfig);
1001
		$regexp = '/(' . implode('|', $intlist) . '):\s/';
1002
		$ifstatus = preg_grep('/status:/', $ifconfig);
1003
		foreach($ifstatus as $status) {
1004
			$int = array_shift($intlist);
1005
			if(stristr($status, "active")) $upints[] = $int;
1006
		}
1007
		break;
1008
	default:
1009
		$upints = pfSense_interface_listget();
1010
		break;
1011
	}
1012
	/* build interface list with netstat */
1013
	$linkinfo = "";
1014
	exec("/usr/bin/netstat -inW -f link | awk '{ print $1, $4 }'", $linkinfo);
1015
	array_shift($linkinfo);
1016
	/* build ip address list with netstat */
1017
	$ipinfo = "";
1018
	exec("/usr/bin/netstat -inW -f inet | awk '{ print $1, $4 }'", $ipinfo);
1019
	array_shift($ipinfo);
1020
	foreach($linkinfo as $link) {
1021
		$friendly = "";
1022
		$alink = explode(" ", $link);
1023
		$ifname = rtrim(trim($alink[0]), '*');
1024
		/* trim out all numbers before checking for vfaces */
1025
		if (!in_array(array_shift(preg_split('/\d/', $ifname)), $vfaces) &&
1026
			!stristr($ifname, "_vlan") && !stristr($ifname, "_wlan")) {
1027
			$toput = array(
1028
					"mac" => trim($alink[1]),
1029
					"up" => in_array($ifname, $upints)
1030
				);
1031
			foreach($ipinfo as $ip) {
1032
				$aip = explode(" ", $ip);
1033
				if($aip[0] == $ifname) {
1034
					$toput['ipaddr'] = $aip[1];
1035
				}
1036
			}
1037
			if (is_array($config['interfaces'])) {
1038
				foreach($config['interfaces'] as $name => $int)
1039
					if($int['if'] == $ifname) $friendly = $name;
1040
			}
1041
			switch($keyby) {
1042
			case "physical":
1043
				if($friendly != "") {
1044
					$toput['friendly'] = $friendly;
1045
				}
1046
				$dmesg_arr = array();
1047
				exec("/sbin/dmesg |grep $ifname | head -n1", $dmesg_arr);
1048
				preg_match_all("/<(.*?)>/i", $dmesg_arr[0], $dmesg);
1049
				$toput['dmesg'] = $dmesg[1][0];
1050
				$iflist[$ifname] = $toput;
1051
				break;
1052
			case "ppp":
1053

    
1054
			case "friendly":
1055
				if($friendly != "") {
1056
					$toput['if'] = $ifname;
1057
					$iflist[$friendly] = $toput;
1058
				}
1059
				break;
1060
			}
1061
		}
1062
	}
1063
	return $iflist;
1064
}
1065

    
1066
/****f* util/log_error
1067
* NAME
1068
*   log_error  - Sends a string to syslog.
1069
* INPUTS
1070
*   $error     - string containing the syslog message.
1071
* RESULT
1072
*   null
1073
******/
1074
function log_error($error) {
1075
	global $g;
1076
	$page = $_SERVER['SCRIPT_NAME'];
1077
	if (empty($page)) {
1078
		$files = get_included_files();
1079
		$page = basename($files[0]);
1080
	}
1081
	syslog(LOG_ERR, "$page: $error");
1082
	if ($g['debug'])
1083
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1084
	return;
1085
}
1086

    
1087
/****f* util/log_auth
1088
* NAME
1089
*   log_auth   - Sends a string to syslog as LOG_AUTH facility
1090
* INPUTS
1091
*   $error     - string containing the syslog message.
1092
* RESULT
1093
*   null
1094
******/
1095
function log_auth($error) {
1096
	global $g;
1097
	$page = $_SERVER['SCRIPT_NAME'];
1098
	syslog(LOG_AUTH, "$page: $error");
1099
	if ($g['debug'])
1100
		syslog(LOG_WARNING, var_dump(debug_backtrace()));
1101
	return;
1102
}
1103

    
1104
/****f* util/exec_command
1105
 * NAME
1106
 *   exec_command - Execute a command and return a string of the result.
1107
 * INPUTS
1108
 *   $command   - String of the command to be executed.
1109
 * RESULT
1110
 *   String containing the command's result.
1111
 * NOTES
1112
 *   This function returns the command's stdout and stderr.
1113
 ******/
1114
function exec_command($command) {
1115
	$output = array();
1116
	exec($command . ' 2>&1 ', $output);
1117
	return(implode("\n", $output));
1118
}
1119

    
1120
/* wrapper for exec() */
1121
function mwexec($command, $mute = false, $clearsigmask = false) {
1122
	global $g;
1123

    
1124
	if ($g['debug']) {
1125
		if (!$_SERVER['REMOTE_ADDR'])
1126
			echo "mwexec(): $command\n";
1127
	}
1128
	$oarr = array();
1129
	$retval = 0;
1130

    
1131
	if ($clearsigmask) {
1132
		$oldset = array();
1133
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1134
	}
1135
	$garbage = exec("$command 2>&1", $oarr, $retval);
1136
	if ($clearsigmask) {
1137
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1138
	}
1139

    
1140
	if(isset($config['system']['developerspew']))
1141
		$mute = false;
1142
	if(($retval <> 0) && ($mute === false)) {
1143
		$output = implode(" ", $oarr);
1144
		log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), $command, $retval, $output));
1145
	}
1146
	return $retval;
1147
}
1148

    
1149
/* wrapper for exec() in background */
1150
function mwexec_bg($command, $clearsigmask = false) {
1151
	global $g;
1152

    
1153
	if ($g['debug']) {
1154
		if (!$_SERVER['REMOTE_ADDR'])
1155
			echo "mwexec(): $command\n";
1156
	}
1157

    
1158
	if ($clearsigmask) {
1159
		$oldset = array();
1160
		pcntl_sigprocmask(SIG_SETMASK, array(), $oldset);
1161
	}
1162
	$_gb = exec("/usr/bin/nohup $command > /dev/null 2>&1 &");
1163
	if ($clearsigmask) {
1164
		pcntl_sigprocmask(SIG_SETMASK, $oldset);
1165
	}
1166
	unset($_gb);
1167
}
1168

    
1169
/* unlink a file, if it exists */
1170
function unlink_if_exists($fn) {
1171
	$to_do = glob($fn);
1172
	if(is_array($to_do)) {
1173
		foreach($to_do as $filename)
1174
			@unlink($filename);
1175
	} else {
1176
		@unlink($fn);
1177
	}
1178
}
1179
/* make a global alias table (for faster lookups) */
1180
function alias_make_table($config) {
1181
	global $aliastable;
1182

    
1183
	$aliastable = array();
1184

    
1185
	if (is_array($config['aliases']['alias'])) {
1186
		foreach ($config['aliases']['alias'] as $alias) {
1187
			if ($alias['name'])
1188
				$aliastable[$alias['name']] = $alias['address'];
1189
		}
1190
	}
1191
}
1192

    
1193
/* check if an alias exists */
1194
function is_alias($name) {
1195
	global $aliastable;
1196

    
1197
	return isset($aliastable[$name]);
1198
}
1199

    
1200
function alias_get_type($name) {
1201
	global $config;
1202

    
1203
	if (is_array($config['aliases']['alias'])) {
1204
		foreach ($config['aliases']['alias'] as $alias) {
1205
			if ($name == $alias['name'])
1206
				return $alias['type'];
1207
		}
1208
	}
1209

    
1210
	return "";
1211
}
1212

    
1213
/* expand a host or network alias, if necessary */
1214
function alias_expand($name) {
1215
	global $aliastable;
1216

    
1217
	if (isset($aliastable[$name]))
1218
		return "\${$name}";
1219
	else if (is_ipaddr($name) || is_subnet($name) || is_port($name))
1220
		return "{$name}";
1221
	else
1222
		return null;
1223
}
1224

    
1225
function alias_expand_urltable($name) {
1226
	global $config;
1227
	$urltable_prefix = "/var/db/aliastables/";
1228
	$urltable_filename = $urltable_prefix . $name . ".txt";
1229

    
1230
	if (is_array($config['aliases']['alias'])) {
1231
		foreach ($config['aliases']['alias'] as $alias) {
1232
			if (preg_match("/urltable/i", $alias['type']) && ($alias['name'] == $name)) {
1233
				if (is_URL($alias["url"]) && file_exists($urltable_filename) && filesize($urltable_filename))
1234
					return $urltable_filename;
1235
				else if (process_alias_urltable($name, $alias["url"], 0, true))
1236
					return $urltable_filename;
1237
			}
1238
		}
1239
	}
1240
	return null;
1241
}
1242

    
1243
function subnet_size($subnet) {
1244
	if (is_subnetv4($subnet)) {
1245
		list ($ip, $bits) = explode("/", $subnet);
1246
		return round(exp(log(2) * (32 - $bits)));
1247
	}
1248
	else if (is_subnetv6($subnet)) {
1249
		list ($ip, $bits) = explode("/", $subnet);
1250
		return round(exp(log(2) * (128 - $bits)));
1251
	}
1252
	else {
1253
		return 0;
1254
	}
1255
}
1256

    
1257
function subnet_expand($subnet) {
1258
	if (is_subnetv4($subnet)) {
1259
		return subnetv4_expand($subnet);
1260
	} else if (is_subnetv6($subnet)) {
1261
		return subnetv6_expand($subnet);
1262
	} else {
1263
		return $subnet;
1264
	}
1265
}
1266

    
1267
function subnetv4_expand($subnet) {
1268
	$result = array();
1269
	list ($ip, $bits) = explode("/", $subnet);
1270
	$net  = ip2long($ip);
1271
	$mask = (0xffffffff << (32 - $bits));
1272
	$net &= $mask;
1273
	$size = round(exp(log(2) * (32 - $bits)));
1274
	for ($i = 0; $i < $size; $i += 1) {
1275
		$result[] = long2ip($net | $i);
1276
	}
1277
	return $result;
1278
}
1279

    
1280
/* find out whether two subnets overlap */
1281
function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
1282

    
1283
	if (!is_numeric($bits1))
1284
		$bits1 = 32;
1285
	if (!is_numeric($bits2))
1286
		$bits2 = 32;
1287

    
1288
	if ($bits1 < $bits2)
1289
		$relbits = $bits1;
1290
	else
1291
		$relbits = $bits2;
1292

    
1293
	$sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
1294
	$sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
1295

    
1296
	return ($sn1 == $sn2);
1297
}
1298

    
1299
/* find out whether two IPv6 subnets overlap */
1300
function check_subnetsv6_overlap($subnet1, $bits1, $subnet2, $bits2) {
1301
	$sub1_min = gen_subnetv6($subnet1, $bits1);
1302
	$sub1_max = gen_subnetv6_max($subnet1, $bits1);
1303
	$sub2_min = gen_subnetv6($subnet2, $bits2);
1304
	$sub2_max = gen_subnetv6_max($subnet2, $bits2);
1305

    
1306
	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));
1307
}
1308

    
1309
/* compare two IP addresses */
1310
function ipcmp($a, $b) {
1311
	if (ip_less_than($a, $b))
1312
		return -1;
1313
	else if (ip_greater_than($a, $b))
1314
		return 1;
1315
	else
1316
		return 0;
1317
}
1318

    
1319
/* return true if $addr is in $subnet, false if not */
1320
function ip_in_subnet($addr,$subnet) {
1321
	if(is_ipaddrv6($addr)) {
1322
		return (Net_IPv6::isInNetmask($addr, $subnet));
1323
	} else { /* XXX: Maybe check for IPv4 */
1324
		list($ip, $mask) = explode('/', $subnet);
1325
		$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
1326
		return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1327
	}
1328
}
1329

    
1330
/* verify (and remove) the digital signature on a file - returns 0 if OK */
1331
function verify_digital_signature($fname) {
1332
	global $g;
1333

    
1334
	if(!file_exists("/usr/local/sbin/gzsig"))
1335
		return 4;
1336

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

    
1340
/* obtain MAC address given an IP address by looking at the ARP table */
1341
function arp_get_mac_by_ip($ip) {
1342
	mwexec("/sbin/ping -c 1 -t 1 {$ip}", true);
1343
	$arpoutput = "";
1344
	exec("/usr/sbin/arp -n {$ip}", $arpoutput);
1345

    
1346
	if ($arpoutput[0]) {
1347
		$arpi = explode(" ", $arpoutput[0]);
1348
		$macaddr = $arpi[3];
1349
		if (is_macaddr($macaddr))
1350
			return $macaddr;
1351
		else
1352
			return false;
1353
	}
1354

    
1355
	return false;
1356
}
1357

    
1358
/* return a fieldname that is safe for xml usage */
1359
function xml_safe_fieldname($fieldname) {
1360
	$replace = array('/', '-', ' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
1361
			 '_', '+', '=', '{', '}', '[', ']', '|', '/', '<', '>', '?',
1362
			 ':', ',', '.', '\'', '\\'
1363
		);
1364
	return strtolower(str_replace($replace, "", $fieldname));
1365
}
1366

    
1367
function mac_format($clientmac) {
1368
	global $config, $cpzone;
1369

    
1370
	$mac = explode(":", $clientmac);
1371
	$mac_format = $cpzone ? $config['captiveportal'][$cpzone]['radmac_format'] : false;
1372

    
1373
	switch($mac_format) {
1374
	case 'singledash':
1375
		return "$mac[0]$mac[1]$mac[2]-$mac[3]$mac[4]$mac[5]";
1376

    
1377
	case 'ietf':
1378
		return "$mac[0]-$mac[1]-$mac[2]-$mac[3]-$mac[4]-$mac[5]";
1379

    
1380
	case 'cisco':
1381
		return "$mac[0]$mac[1].$mac[2]$mac[3].$mac[4]$mac[5]";
1382

    
1383
	case 'unformatted':
1384
		return "$mac[0]$mac[1]$mac[2]$mac[3]$mac[4]$mac[5]";
1385

    
1386
	default:
1387
		return $clientmac;
1388
	}
1389
}
1390

    
1391
function resolve_retry($hostname, $retries = 5) {
1392

    
1393
	if (is_ipaddr($hostname))
1394
		return $hostname;
1395

    
1396
	for ($i = 0; $i < $retries; $i++) {
1397
		// FIXME: gethostbyname does not work for AAAA hostnames, boo, hiss
1398
		$ip = gethostbyname($hostname);
1399

    
1400
		if ($ip && $ip != $hostname) {
1401
			/* success */
1402
			return $ip;
1403
		}
1404

    
1405
		sleep(1);
1406
	}
1407

    
1408
	return false;
1409
}
1410

    
1411
function format_bytes($bytes) {
1412
	if ($bytes >= 1073741824) {
1413
		return sprintf("%.2f GB", $bytes/1073741824);
1414
	} else if ($bytes >= 1048576) {
1415
		return sprintf("%.2f MB", $bytes/1048576);
1416
	} else if ($bytes >= 1024) {
1417
		return sprintf("%.0f KB", $bytes/1024);
1418
	} else {
1419
		return sprintf("%d bytes", $bytes);
1420
	}
1421
}
1422

    
1423
function update_filter_reload_status($text) {
1424
	global $g;
1425

    
1426
	file_put_contents("{$g['varrun_path']}/filter_reload_status", $text);
1427
}
1428

    
1429
/****** util/return_dir_as_array
1430
 * NAME
1431
 *   return_dir_as_array - Return a directory's contents as an array.
1432
 * INPUTS
1433
 *   $dir          - string containing the path to the desired directory.
1434
 *   $filter_regex - string containing a regular expression to filter file names. Default empty.
1435
 * RESULT
1436
 *   $dir_array - array containing the directory's contents. This array will be empty if the path specified is invalid.
1437
 ******/
1438
function return_dir_as_array($dir, $filter_regex = '') {
1439
	$dir_array = array();
1440
	if (is_dir($dir)) {
1441
		if ($dh = opendir($dir)) {
1442
			while (($file = readdir($dh)) !== false) {
1443
				if (($file == ".") || ($file == ".."))
1444
					continue;
1445

    
1446
				if (empty($filter_regex) || preg_match($filter_regex, $file))
1447
					array_push($dir_array, $file);
1448
			}
1449
			closedir($dh);
1450
		}
1451
	}
1452
	return $dir_array;
1453
}
1454

    
1455
function run_plugins($directory) {
1456
	global $config, $g;
1457

    
1458
	/* process packager manager custom rules */
1459
	$files = return_dir_as_array($directory);
1460
	if (is_array($files)) {
1461
		foreach ($files as $file) {
1462
			if (stristr($file, ".sh") == true)
1463
				mwexec($directory . $file . " start");
1464
			else if (!is_dir($directory . "/" . $file) && stristr($file,".inc"))
1465
				require_once($directory . "/" . $file);
1466
		}
1467
	}
1468
}
1469

    
1470
/*
1471
 *    safe_mkdir($path, $mode = 0755)
1472
 *    create directory if it doesn't already exist and isn't a file!
1473
 */
1474
function safe_mkdir($path, $mode=0755) {
1475
	global $g;
1476

    
1477
	if (!is_file($path) && !is_dir($path)) {
1478
		return @mkdir($path, $mode, true);
1479
	} else {
1480
		return false;
1481
	}
1482
}
1483

    
1484
/*
1485
 * make_dirs($path, $mode = 0755)
1486
 * create directory tree recursively (mkdir -p)
1487
 */
1488
function make_dirs($path, $mode = 0755) {
1489
	$base = '';
1490
	foreach (explode('/', $path) as $dir) {
1491
		$base .= "/$dir";
1492
		if (!is_dir($base)) {
1493
			if (!@mkdir($base, $mode))
1494
				return false;
1495
		}
1496
	}
1497
	return true;
1498
}
1499

    
1500
/*
1501
 * get_sysctl($names)
1502
 * Get values of sysctl OID's listed in $names (accepts an array or a single
1503
 * name) and return an array of key/value pairs set for those that exist
1504
 */
1505
function get_sysctl($names) {
1506
	if (empty($names))
1507
		return array();
1508

    
1509
	if (is_array($names)) {
1510
		$name_list = array();
1511
		foreach ($names as $name) {
1512
			$name_list[] = escapeshellarg($name);
1513
		}
1514
	} else
1515
		$name_list = array(escapeshellarg($names));
1516

    
1517
	exec("/sbin/sysctl -i " . implode(" ", $name_list), $output);
1518
	$values = array();
1519
	foreach ($output as $line) {
1520
		$line = explode(": ", $line, 2);
1521
		if (count($line) == 2)
1522
			$values[$line[0]] = $line[1];
1523
	}
1524

    
1525
	return $values;
1526
}
1527

    
1528
/*
1529
 * set_sysctl($value_list)
1530
 * Set sysctl OID's listed as key/value pairs and return
1531
 * an array with keys set for those that succeeded
1532
 */
1533
function set_sysctl($values) {
1534
	if (empty($values))
1535
		return array();
1536

    
1537
	$value_list = array();
1538
	foreach ($values as $key => $value) {
1539
		$value_list[] = escapeshellarg($key) . "=" . escapeshellarg($value);
1540
	}
1541

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

    
1544
	/* Retry individually if failed (one or more read-only) */
1545
	if ($success <> 0 && count($value_list) > 1) {
1546
		foreach ($value_list as $value) {
1547
			exec("/sbin/sysctl -i " . $value, $output);
1548
		}
1549
	}
1550

    
1551
	$ret = array();
1552
	foreach ($output as $line) {
1553
		$line = explode(": ", $line, 2);
1554
		if (count($line) == 2)
1555
			$ret[$line[0]] = true;
1556
	}
1557

    
1558
	return $ret;
1559
}
1560

    
1561
/*
1562
 *     get_memory()
1563
 *     returns an array listing the amount of
1564
 *     memory installed in the hardware
1565
 *     [0] net memory available for the OS (FreeBSD) after some is taken by BIOS, video or whatever - e.g. 235 MBytes
1566
 *     [1] real (actual) memory of the system, should be the size of the RAM card/s - e.g. 256 MBytes
1567
 */
1568
function get_memory() {
1569

    
1570
	$physmem = trim(`sysctl -n hw.physmem`, " \n");
1571
	$realmem = trim(`sysctl -n hw.realmem`, " \n");
1572
	/* convert from bytes to megabytes */
1573
	return array(($physmem/1048576),($realmem/1048576));
1574
}
1575

    
1576
function mute_kernel_msgs() {
1577
	global $config;
1578
	// Do not mute serial console.  The kernel gets very very cranky
1579
	// and will start dishing you cannot control tty errors.
1580
	switch (trim(file_get_contents("/etc/platform"))) {
1581
		case "nanobsd":
1582
		case "jail":
1583
			return;
1584
	}
1585
	if($config['system']['enableserial'])
1586
		return;
1587
	exec("/sbin/conscontrol mute on");
1588
}
1589

    
1590
function unmute_kernel_msgs() {
1591
	global $config;
1592
	// Do not mute serial console.  The kernel gets very very cranky
1593
	// and will start dishing you cannot control tty errors.
1594
	switch (trim(file_get_contents("/etc/platform"))) {
1595
		case "nanobsd":
1596
		case "jail":
1597
			return;
1598
	}
1599
	exec("/sbin/conscontrol mute off");
1600
}
1601

    
1602
function start_devd() {
1603
	global $g;
1604

    
1605
	if ($g['platform'] == 'jail')
1606
		return;
1607
	exec("/sbin/devd");
1608
	sleep(1);
1609
}
1610

    
1611
function is_interface_vlan_mismatch() {
1612
	global $config, $g;
1613

    
1614
	if (is_array($config['vlans']['vlan'])) {
1615
		foreach ($config['vlans']['vlan'] as $vlan) {
1616
			if (does_interface_exist($vlan['if']) == false)
1617
				return true;
1618
		}
1619
	}
1620

    
1621
	return false;
1622
}
1623

    
1624
function is_interface_mismatch() {
1625
	global $config, $g;
1626

    
1627
	$do_assign = false;
1628
	$i = 0;
1629
	$missing_interfaces = array();
1630
	if (is_array($config['interfaces'])) {
1631
		foreach ($config['interfaces'] as $ifname => $ifcfg) {
1632
			if (preg_match("/^enc|^cua|^tun|^tap|^l2tp|^pptp|^ppp|^ovpn|^gif|^gre|^lagg|^bridge|vlan|_wlan/i", $ifcfg['if'])) {
1633
				// Do not check these interfaces.
1634
				$i++;
1635
				continue;
1636
			}
1637
			else if (does_interface_exist($ifcfg['if']) == false) {
1638
				$missing_interfaces[] = $ifcfg['if'];
1639
				$do_assign = true;
1640
			} else
1641
				$i++;
1642
		}
1643
	}
1644

    
1645
	if ($g['minimum_nic_count'] > $i) {
1646
		$do_assign = true;
1647
	} else if (file_exists("{$g['tmp_path']}/assign_complete"))
1648
		$do_assign = false;
1649

    
1650
	if (!empty($missing_interfaces) && $do_assign)
1651
		file_put_contents("{$g['tmp_path']}/missing_interfaces", implode(' ', $missing_interfaces));
1652
	else
1653
		@unlink("{$g['tmp_path']}/missing_interfaces");
1654

    
1655
	return $do_assign;
1656
}
1657

    
1658
/* sync carp entries to other firewalls */
1659
function carp_sync_client() {
1660
	global $g;
1661
	send_event("filter sync");
1662
}
1663

    
1664
/****f* util/isAjax
1665
 * NAME
1666
 *   isAjax - reports if the request is driven from prototype
1667
 * INPUTS
1668
 *   none
1669
 * RESULT
1670
 *   true/false
1671
 ******/
1672
function isAjax() {
1673
	return isset ($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
1674
}
1675

    
1676
/****f* util/timeout
1677
 * NAME
1678
 *   timeout - console input with timeout countdown. Note: erases 2 char of screen for timer. Leave space.
1679
 * INPUTS
1680
 *   optional, seconds to wait before timeout. Default 9 seconds.
1681
 * RESULT
1682
 *   returns 1 char of user input or null if no input.
1683
 ******/
1684
function timeout($timer = 9) {
1685
	while(!isset($key)) {
1686
		if ($timer >= 9) { echo chr(8) . chr(8) . ($timer==9 ? chr(32) : null)  . "{$timer}";  }
1687
		else { echo chr(8). "{$timer}"; }
1688
		`/bin/stty -icanon min 0 time 25`;
1689
		$key = trim(`KEY=\`dd count=1 2>/dev/null\`; echo \$KEY`);
1690
		`/bin/stty icanon`;
1691
		if ($key == '')
1692
			unset($key);
1693
		$timer--;
1694
		if ($timer == 0)
1695
			break;
1696
	}
1697
	return $key;
1698
}
1699

    
1700
/****f* util/msort
1701
 * NAME
1702
 *   msort - sort array
1703
 * INPUTS
1704
 *   $array to be sorted, field to sort by, direction of sort
1705
 * RESULT
1706
 *   returns newly sorted array
1707
 ******/
1708
function msort($array, $id="id", $sort_ascending=true) {
1709
	$temp_array = array();
1710
	while(count($array)>0) {
1711
		$lowest_id = 0;
1712
		$index=0;
1713
		foreach ($array as $item) {
1714
			if (isset($item[$id])) {
1715
				if ($array[$lowest_id][$id]) {
1716
					if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
1717
						$lowest_id = $index;
1718
					}
1719
				}
1720
			}
1721
			$index++;
1722
		}
1723
		$temp_array[] = $array[$lowest_id];
1724
		$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
1725
	}
1726
	if ($sort_ascending) {
1727
		return $temp_array;
1728
	} else {
1729
		return array_reverse($temp_array);
1730
	}
1731
}
1732

    
1733
/****f* util/color
1734
 * NAME
1735
 *   color - outputs a color code to the ansi terminal if supported
1736
 * INPUTS
1737
 *   color code or color name
1738
 * RESULT
1739
 *   Outputs the ansi color sequence for the color specified.  Default resets terminal.
1740
 ******/
1741
function color($color = "0m") {
1742
	/*
1743
		Color codes available:
1744
		 0m reset; clears all colors and styles (to white on black)
1745
		 1m bold on (see below)
1746
		 3m italics on
1747
		 4m underline on
1748
		 7m inverse on; reverses foreground & background colors
1749
		 9m strikethrough on
1750
		 22m bold off (see below)
1751
		 23m italics off
1752
		 24m underline off
1753
		 27m inverse off
1754
		 29m strikethrough off
1755
		 30m set foreground color to black
1756
		 31m set foreground color to red
1757
		 32m set foreground color to green
1758
		 33m set foreground color to yellow
1759
		 34m set foreground color to blue
1760
		 35m set foreground color to magenta (purple)
1761
		 36m set foreground color to cyan
1762
		 37m set foreground color to white
1763
		 40m  set background color to black
1764
		 41m set background color to red
1765
		 42m set background color to green
1766
		 43m set background color to yellow
1767
		 44m set background color to blue
1768
		 45m set background color to magenta (purple)
1769
		 46m set background color to cyan
1770
		 47m set background color to white
1771
		 49m set background color to default (black)
1772
	*/
1773
	// Allow caching of TERM to
1774
	// speedup subequence requests.
1775
	global $TERM;
1776
	if(!$TERM)
1777
		$TERM=`/usr/bin/env | grep color`;
1778
	if(!$TERM)
1779
		$TERM=`/usr/bin/env | grep cons25`;
1780
	if($TERM) {
1781
		$ESCAPE=chr(27);
1782
		switch ($color) {
1783
			case "black":
1784
				return "{$ESCAPE}[30m";
1785
			case "red":
1786
				return "{$ESCAPE}[31m";
1787
			case "green":
1788
				return "{$ESCAPE}[32m";
1789
			case "yellow":
1790
				return "{$ESCAPE}[33m";
1791
			case "blue":
1792
				return "{$ESCAPE}[34m";
1793
			case "magenta":
1794
				return "{$ESCAPE}[35m";
1795
			case "cyan":
1796
				return "{$ESCAPE}[36m";
1797
			case "white":
1798
				return "{$ESCAPE}[37m";
1799
			case "default":
1800
				return "{$ESCAPE}[39m";
1801
		}
1802
		return "{$ESCAPE}[{$color}";
1803
	}
1804
}
1805

    
1806
/****f* util/is_URL
1807
 * NAME
1808
 *   is_URL
1809
 * INPUTS
1810
 *   string to check
1811
 * RESULT
1812
 *   Returns true if item is a URL
1813
 ******/
1814
function is_URL($url) {
1815
	$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
1816
	if($match)
1817
		return true;
1818
	return false;
1819
}
1820

    
1821
function is_file_included($file = "") {
1822
	$files = get_included_files();
1823
	if (in_array($file, $files))
1824
		return true;
1825

    
1826
	return false;
1827
}
1828

    
1829
/*
1830
 * Replace a value on a deep associative array using regex
1831
 */
1832
function array_replace_values_recursive($data, $match, $replace) {
1833
	if (empty($data))
1834
		return $data;
1835

    
1836
	if (is_string($data))
1837
		$data = preg_replace("/{$match}/", $replace, $data);
1838
	else if (is_array($data))
1839
		foreach ($data as $k => $v)
1840
			$data[$k] = array_replace_values_recursive($v, $match, $replace);
1841

    
1842
	return $data;
1843
}
1844

    
1845
/*
1846
	This function was borrowed from a comment on PHP.net at the following URL:
1847
	http://www.php.net/manual/en/function.array-merge-recursive.php#73843
1848
 */
1849
function array_merge_recursive_unique($array0, $array1) {
1850

    
1851
	$arrays = func_get_args();
1852
	$remains = $arrays;
1853

    
1854
	// We walk through each arrays and put value in the results (without
1855
	// considering previous value).
1856
	$result = array();
1857

    
1858
	// loop available array
1859
	foreach($arrays as $array) {
1860

    
1861
		// The first remaining array is $array. We are processing it. So
1862
		// we remove it from remaing arrays.
1863
		array_shift($remains);
1864

    
1865
		// We don't care non array param, like array_merge since PHP 5.0.
1866
		if(is_array($array)) {
1867
			// Loop values
1868
			foreach($array as $key => $value) {
1869
				if(is_array($value)) {
1870
					// we gather all remaining arrays that have such key available
1871
					$args = array();
1872
					foreach($remains as $remain) {
1873
						if(array_key_exists($key, $remain)) {
1874
							array_push($args, $remain[$key]);
1875
						}
1876
					}
1877

    
1878
					if(count($args) > 2) {
1879
						// put the recursion
1880
						$result[$key] = call_user_func_array(__FUNCTION__, $args);
1881
					} else {
1882
						foreach($value as $vkey => $vval) {
1883
							$result[$key][$vkey] = $vval;
1884
						}
1885
					}
1886
				} else {
1887
					// simply put the value
1888
					$result[$key] = $value;
1889
				}
1890
			}
1891
		}
1892
	}
1893
	return $result;
1894
}
1895

    
1896

    
1897
/*
1898
 * converts a string like "a,b,c,d"
1899
 * into an array like array("a" => "b", "c" => "d")
1900
 */
1901
function explode_assoc($delimiter, $string) {
1902
	$array = explode($delimiter, $string);
1903
	$result = array();
1904
	$numkeys = floor(count($array) / 2);
1905
	for ($i = 0; $i < $numkeys; $i += 1) {
1906
		$result[$array[$i * 2]] = $array[$i * 2 + 1];
1907
	}
1908
	return $result;
1909
}
1910

    
1911
function get_staticroutes($returnsubnetsonly = false, $returnhostnames = false) {
1912
	global $config, $aliastable;
1913

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

    
1918
	$allstaticroutes = array();
1919
	$allsubnets = array();
1920
	/* Loop through routes and expand aliases as we find them. */
1921
	foreach ($config['staticroutes']['route'] as $route) {
1922
		if (is_alias($route['network'])) {
1923
			if (!isset($aliastable[$route['network']]))
1924
				continue;
1925

    
1926
			$subnets = preg_split('/\s+/', $aliastable[$route['network']]);
1927
			foreach ($subnets as $net) {
1928
				if (!is_subnet($net)) {
1929
					if (is_ipaddrv4($net))
1930
						$net .= "/32";
1931
					else if (is_ipaddrv6($net))
1932
						$net .= "/128";
1933
					else if ($returnhostnames === false || !is_fqdn($net))
1934
						continue;
1935
				}
1936
				$temproute = $route;
1937
				$temproute['network'] = $net;
1938
				$allstaticroutes[] = $temproute;
1939
				$allsubnets[] = $net;
1940
			}
1941
		} elseif (is_subnet($route['network'])) {
1942
			$allstaticroutes[] = $route;
1943
			$allsubnets[] = $route['network'];
1944
		}
1945
	}
1946
	if ($returnsubnetsonly)
1947
		return $allsubnets;
1948
	else
1949
		return $allstaticroutes;
1950
}
1951

    
1952
/****f* util/get_alias_list
1953
 * NAME
1954
 *   get_alias_list - Provide a list of aliases.
1955
 * INPUTS
1956
 *   $type          - Optional, can be a string or array specifying what type(s) of aliases you need.
1957
 * RESULT
1958
 *   Array containing list of aliases.
1959
 *   If $type is unspecified, all aliases are returned.
1960
 *   If $type is a string, all aliases of the type specified in $type are returned.
1961
 *   If $type is an array, all aliases of any type specified in any element of $type are returned.
1962
 */
1963
function get_alias_list($type = null) {
1964
	global $config;
1965
	$result = array();
1966
	if ($config['aliases']['alias'] <> "" && is_array($config['aliases']['alias'])) {
1967
		foreach ($config['aliases']['alias'] as $alias) {
1968
			if ($type === null) {
1969
				$result[] = $alias['name'];
1970
			}
1971
			else if (is_array($type)) {
1972
				if (in_array($alias['type'], $type)) {
1973
					$result[] = $alias['name'];
1974
				}
1975
			}
1976
			else if ($type === $alias['type']) {
1977
				$result[] = $alias['name'];
1978
			}
1979
		}
1980
	}
1981
	return $result;
1982
}
1983

    
1984
/* returns an array consisting of every element of $haystack that is not equal to $needle. */
1985
function array_exclude($needle, $haystack) {
1986
	$result = array();
1987
	if (is_array($haystack)) {
1988
		foreach ($haystack as $thing) {
1989
			if ($needle !== $thing) {
1990
				$result[] = $thing;
1991
			}
1992
		}
1993
	}
1994
	return $result;
1995
}
1996

    
1997
function setup_library_paths() {
1998
	$current_library_paths = explode(":", exec("/sbin/ldconfig -r | /usr/bin/grep 'search directories' | /usr/bin/awk '{print $3;}'"));
1999
	$pbi_library_paths = array_merge(glob("/usr/pbi/*/lib", GLOB_ONLYDIR), glob("/usr/pbi/*/lib/*", GLOB_ONLYDIR));
2000
	foreach ($pbi_library_paths as $pbilib) {
2001
		if (!in_array($pbilib, $current_library_paths))
2002
			exec("/sbin/ldconfig -m {$pbilib}");
2003
	}
2004
}
2005

    
2006
function get_current_theme() {
2007
	global $config, $g;
2008
	/*
2009
	 *   if user has selected a custom template, use it.
2010
	 *   otherwise default to pfsense tempalte
2011
	 */
2012
	if (($g["disablethemeselection"] === true) && !empty($g["default_theme"]) && (is_dir($g["www_path"].'/themes/'.$g["default_theme"])))
2013
		$theme = $g["default_theme"];
2014
	elseif($config['theme'] <> "" && (is_dir($g["www_path"].'/themes/'.$config['theme'])))
2015
		$theme = $config['theme'];
2016
	else
2017
		$theme = "pfsense";
2018
	/*
2019
	 *  If this device is an apple ipod/iphone
2020
	 *  switch the theme to one that works with it.
2021
	 */
2022
	$lowres_ua = array("iPhone", "iPod", "iPad", "Android", "BlackBerry", "Opera Mini", "Opera Mobi", "PlayBook", "IEMobile");
2023
	foreach($lowres_ua as $useragent)
2024
		if(strstr($_SERVER['HTTP_USER_AGENT'], $useragent))
2025
			$theme = (empty($g['theme_lowres']) && (is_dir($g["www_path"].'/themes/'.$g['theme_lowres']))) ? "pfsense" : $g['theme_lowres'];
2026
	return $theme;
2027
}
2028

    
2029
?>
(54-54/66)