Project

General

Profile

Download (101 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * pfsense-utils.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

    
24
/****f* pfsense-utils/have_natpfruleint_access
25
 * NAME
26
 *   have_natpfruleint_access
27
 * INPUTS
28
 *	none
29
 * RESULT
30
 *   returns true if user has access to edit a specific firewall nat port forward interface
31
 ******/
32
function have_natpfruleint_access($if) {
33
	$security_url = "firewall_nat_edit.php?if=". strtolower($if);
34
	if (isAllowedPage($security_url, $allowed)) {
35
		return true;
36
	}
37
	return false;
38
}
39

    
40
/****f* pfsense-utils/have_ruleint_access
41
 * NAME
42
 *   have_ruleint_access
43
 * INPUTS
44
 *	none
45
 * RESULT
46
 *   returns true if user has access to edit a specific firewall interface
47
 ******/
48
function have_ruleint_access($if) {
49
	$security_url = "firewall_rules.php?if=". strtolower($if);
50
	if (isAllowedPage($security_url)) {
51
		return true;
52
	}
53
	return false;
54
}
55

    
56
/****f* pfsense-utils/does_url_exist
57
 * NAME
58
 *   does_url_exist
59
 * INPUTS
60
 *	none
61
 * RESULT
62
 *   returns true if a url is available
63
 ******/
64
function does_url_exist($url) {
65
	$fd = fopen("$url", "r");
66
	if ($fd) {
67
		fclose($fd);
68
		return true;
69
	} else {
70
		return false;
71
	}
72
}
73

    
74
/****f* pfsense-utils/is_private_ip
75
 * NAME
76
 *   is_private_ip
77
 * INPUTS
78
 *	none
79
 * RESULT
80
 *   returns true if an ip address is in a private range
81
 ******/
82
function is_private_ip($iptocheck) {
83
	$isprivate = false;
84
	$ip_private_list = array(
85
		"10.0.0.0/8",
86
		"100.64.0.0/10",
87
		"172.16.0.0/12",
88
		"192.168.0.0/16",
89
	);
90
	foreach ($ip_private_list as $private) {
91
		if (ip_in_subnet($iptocheck, $private) == true) {
92
			$isprivate = true;
93
		}
94
	}
95
	return $isprivate;
96
}
97

    
98
/****f* pfsense-utils/get_tmp_file
99
 * NAME
100
 *   get_tmp_file
101
 * INPUTS
102
 *	none
103
 * RESULT
104
 *   returns a temporary filename
105
 ******/
106
function get_tmp_file() {
107
	global $g;
108
	return "{$g['tmp_path']}/tmp-" . time();
109
}
110

    
111
/* Stub for deprecated function
112
 * See https://redmine.pfsense.org/issues/10931 */
113
function get_dns_servers() {
114
	return get_dns_nameservers(false, true);
115
}
116

    
117
/****f* pfsense-utils/pfSenseHeader
118
 * NAME
119
 *   pfSenseHeader
120
 * INPUTS
121
 *   none
122
 * RESULT
123
 *   Javascript header change or browser Location:
124
 ******/
125
function pfSenseHeader($text) {
126
	global $_SERVER;
127
	if (isAjax()) {
128
		if ($_SERVER['HTTPS'] == "on") {
129
			$protocol = "https";
130
		} else {
131
			$protocol = "http";
132
		}
133

    
134
		$port = ":{$_SERVER['SERVER_PORT']}";
135
		if ($_SERVER['SERVER_PORT'] == "80" && $protocol == "http") {
136
			$port = "";
137
		}
138
		if ($_SERVER['SERVER_PORT'] == "443" && $protocol == "https") {
139
			$port = "";
140
		}
141
		$complete_url = "{$protocol}://{$_SERVER['HTTP_HOST']}{$port}/{$text}";
142
		echo "\ndocument.location.href = '{$complete_url}';\n";
143
	} else {
144
		header("Location: $text");
145
	}
146
}
147

    
148
/****f* pfsense-utils/get_css_files
149
 * NAME
150
 *   get_css_files - get a list of the available CSS files (themes)
151
 * INPUTS
152
 *   none
153
 * RESULT
154
 *   $csslist - an array of the CSS files
155
 ******/
156
function get_css_files() {
157
	$csslist = array();
158

    
159
	// List pfSense files, then any BETA files followed by any user-contributed files
160
	$cssfiles = glob("/usr/local/www/css/*.css");
161

    
162
	if (is_array($cssfiles)) {
163
		arsort($cssfiles);
164
		$usrcss = $pfscss = $betacss = array();
165

    
166
		foreach ($cssfiles as $css) {
167
			// Don't display any login/logo page related CSS files
168
			if (strpos($css, "login") == 0 &&
169
			    strpos($css, "logo") == 0) {
170
				if (strpos($css, "BETA") != 0) {
171
					array_push($betacss, $css);
172
				} else if (strpos($css, "pfSense") != 0) {
173
					array_push($pfscss, $css);
174
				} else {
175
					array_push($usrcss, $css);
176
				}
177
			}
178
		}
179

    
180
		$css = array_merge($pfscss, $betacss, $usrcss);
181

    
182
		foreach ($css as $file) {
183
			$file = basename($file);
184
			$csslist[$file] = pathinfo($file, PATHINFO_FILENAME);
185
		}
186
	}
187
	return $csslist;
188
}
189

    
190
/****f* pfsense-utils/gen_webguicss_field
191
 * NAME
192
 *   gen_webguicss_field
193
 * INPUTS
194
 *   Pointer to section object
195
 *   Initial value for the field
196
 * RESULT
197
 *   no return value, section object is updated
198
 ******/
199
function gen_webguicss_field(&$section, $value) {
200

    
201
	$csslist = get_css_files();
202

    
203
	if (!isset($csslist[$value])) {
204
		$value = "pfSense.css";
205
	}
206

    
207
	$section->addInput(new Form_Select(
208
		'webguicss',
209
		'Theme',
210
		$value,
211
		$csslist
212
	))->setHelp('Choose an alternative css file (if installed) to change the appearance of the webConfigurator. css files are located in /usr/local/www/css/%s', '<span id="csstxt"></span>');
213
}
214
function validate_webguicss_field(&$input_errors, $value) {
215
	$csslist = get_css_files();
216
	if (!isset($csslist[$value])) {
217
		$input_errors[] = gettext("The submitted Theme could not be found. Pick a different theme.");
218
	}
219
}
220

    
221
/****f* pfsense-utils/gen_webguifixedmenu_field
222
 * NAME
223
 *   gen_webguifixedmenu_field
224
 * INPUTS
225
 *   Pointer to section object
226
 *   Initial value for the field
227
 * RESULT
228
 *   no return value, section object is updated
229
 ******/
230
function gen_webguifixedmenu_field(&$section, $value) {
231

    
232
	$section->addInput(new Form_Select(
233
		'webguifixedmenu',
234
		'Top Navigation',
235
		$value,
236
		["" => gettext("Scrolls with page"), "fixed" => gettext("Fixed (Remains visible at top of page)")]
237
	))->setHelp("The fixed option is intended for large screens only.");
238
}
239
function validate_webguifixedmenu_field(&$input_errors, $value) {
240
	$valid_values = array("", "fixed");
241
	if (!in_array($value, $valid_values)) {
242
		$input_errors[] = gettext("The submitted Top Navigation value is invalid.");
243
	}
244
}
245

    
246
/****f* pfsense-utils/gen_webguihostnamemenu_field
247
 * NAME
248
 *   gen_webguihostnamemenu_field
249
 * INPUTS
250
 *   Pointer to section object
251
 *   Initial value for the field
252
 * RESULT
253
 *   no return value, section object is updated
254
 ******/
255
function gen_webguihostnamemenu_field(&$section, $value) {
256

    
257
	$section->addInput(new Form_Select(
258
		'webguihostnamemenu',
259
		'Hostname in Menu',
260
		$value,
261
		["" => gettext("Default (No hostname)"), "hostonly" => gettext("Hostname only"), "fqdn" => gettext("Fully Qualified Domain Name")]
262
	))->setHelp("Replaces the Help menu title in the Navbar with the system hostname or FQDN.");
263
}
264
function validate_webguihostnamemenu_field(&$input_errors, $value) {
265
	$valid_values = array("", "hostonly", "fqdn");
266
	if (!in_array($value, $valid_values)) {
267
		$input_errors[] = gettext("The submitted Hostname in Menu value is invalid.");
268
	}
269
}
270

    
271
/****f* pfsense-utils/gen_dashboardcolumns_field
272
 * NAME
273
 *   gen_dashboardcolumns_field
274
 * INPUTS
275
 *   Pointer to section object
276
 *   Initial value for the field
277
 * RESULT
278
 *   no return value, section object is updated
279
 ******/
280
function gen_dashboardcolumns_field(&$section, $value) {
281

    
282
	if (((int) $value < 1) || ((int) $value > 6)) {
283
		$value = 2;
284
	}
285

    
286
	$section->addInput(new Form_Input(
287
		'dashboardcolumns',
288
		'Dashboard Columns',
289
		'number',
290
		$value,
291
		['min' => 1, 'max' => 6]
292
	));
293
}
294
function validate_dashboardcolumns_field(&$input_errors, $value) {
295
	if (!is_numericint($value) || ((int) $value < 1) || ((int) $value > 6)) {
296
		$input_errors[] = gettext("The submitted Dashboard Columns value is invalid.");
297
	}
298
}
299

    
300
/****f* pfsense-utils/gen_interfacessort_field
301
 * NAME
302
 *   gen_interfacessort_field
303
 * INPUTS
304
 *   Pointer to section object
305
 *   Initial value for the field
306
 * RESULT
307
 *   no return value, section object is updated
308
 ******/
309
function gen_interfacessort_field(&$section, $value) {
310

    
311
	$section->addInput(new Form_Checkbox(
312
		'interfacessort',
313
		'Interfaces Sort',
314
		'Sort Alphabetically',
315
		$value
316
	))->setHelp('If selected, lists of interfaces will be sorted by description, otherwise they are listed wan,lan,optn...');
317
}
318

    
319
/****f* pfsense-utils/gen_associatedpanels_fields
320
 * NAME
321
 *   gen_associatedpanels_fields
322
 * INPUTS
323
 *   Pointer to section object
324
 *   Initial value for each of the fields
325
 * RESULT
326
 *   no return value, section object is updated
327
 ******/
328
function gen_associatedpanels_fields(&$section, $value1, $value2, $value3, $value4) {
329

    
330
	$group = new Form_Group('Associated Panels Show/Hide');
331

    
332
	$group->add(new Form_Checkbox(
333
		'dashboardavailablewidgetspanel',
334
		null,
335
		'Available Widgets',
336
		$value1
337
		))->setHelp('Show the Available Widgets panel on the Dashboard.');
338

    
339
	$group->add(new Form_Checkbox(
340
		'systemlogsfilterpanel',
341
		null,
342
		'Log Filter',
343
		$value2
344
	))->setHelp('Show the Log Filter panel in System Logs.');
345

    
346
	$group->add(new Form_Checkbox(
347
		'systemlogsmanagelogpanel',
348
		null,
349
		'Manage Log',
350
		$value3
351
	))->setHelp('Show the Manage Log panel in System Logs.');
352

    
353
	$group->add(new Form_Checkbox(
354
		'statusmonitoringsettingspanel',
355
		null,
356
		'Monitoring Settings',
357
		$value4
358
	))->setHelp('Show the Settings panel in Status Monitoring.');
359

    
360
	$group->setHelp('These options allow certain panels to be automatically hidden on page load. A control is provided in the title bar to un-hide the panel.');
361

    
362
	$section->add($group);
363
}
364

    
365
/****f* pfsense-utils/gen_webguileftcolumnhyper_field
366
 * NAME
367
 *   gen_webguileftcolumnhyper_field
368
 * INPUTS
369
 *   Pointer to section object
370
 *   Initial value for the field
371
 * RESULT
372
 *   no return value, section object is updated
373
 ******/
374
function gen_webguileftcolumnhyper_field(&$section, $value) {
375

    
376
	$section->addInput(new Form_Checkbox(
377
		'webguileftcolumnhyper',
378
		'Left Column Labels',
379
		'Active',
380
		$value
381
	))->setHelp('If selected, clicking a label in the left column will select/toggle the first item of the group.');
382
}
383

    
384
/****f* pfsense-utils/gen_disablealiaspopupdetail_field
385
 * NAME
386
 *   gen_disablealiaspopupdetail_field
387
 * INPUTS
388
 *   Pointer to section object
389
 *   Initial value for the field
390
 * RESULT
391
 *   no return value, section object is updated
392
 ******/
393
function gen_disablealiaspopupdetail_field(&$section, $value) {
394

    
395
	$section->addInput(new Form_Checkbox(
396
		'disablealiaspopupdetail',
397
		'Alias Popups',
398
		'Disable details in alias popups',
399
		$value
400
	))->setHelp('If selected, the details in alias popups will not be shown, just the alias description (e.g. in Firewall Rules).');
401
}
402

    
403
/****f* pfsense-utils/gen_pagenamefirst_field
404
 * NAME
405
 *   gen_pagenamefirst_field
406
 * INPUTS
407
 *   Pointer to section object
408
 *   Initial value for the field
409
 * RESULT
410
 *   no return value, section object is updated
411
 ******/
412
function gen_pagenamefirst_field(&$section, $value) {
413

    
414
	$section->addInput(new Form_Checkbox(
415
		'pagenamefirst',
416
		'Browser tab text',
417
		'Display page name first in browser tab',
418
		$value
419
	))->setHelp('When this is unchecked, the browser tab shows the host name followed '.
420
		'by the current page. Check this box to display the current page followed by the '.
421
		'host name.');
422
}
423

    
424
/****f* pfsense-utils/gen_user_settings_fields
425
 * NAME
426
 *   gen_user_settings_fields
427
 * INPUTS
428
 *   Pointer to section object
429
 *   Array of initial values for the fields
430
 * RESULT
431
 *   no return value, section object is updated
432
 ******/
433
function gen_user_settings_fields(&$section, $pconfig) {
434

    
435
	gen_webguicss_field($section, $pconfig['webguicss']);
436
	gen_webguifixedmenu_field($section, $pconfig['webguifixedmenu']);
437
	gen_webguihostnamemenu_field($section, $pconfig['webguihostnamemenu']);
438
	gen_dashboardcolumns_field($section, $pconfig['dashboardcolumns']);
439
	gen_interfacessort_field($section, $pconfig['interfacessort']);
440
	gen_associatedpanels_fields(
441
		$section,
442
		$pconfig['dashboardavailablewidgetspanel'],
443
		$pconfig['systemlogsfilterpanel'],
444
		$pconfig['systemlogsmanagelogpanel'],
445
		$pconfig['statusmonitoringsettingspanel']);
446
	gen_webguileftcolumnhyper_field($section, $pconfig['webguileftcolumnhyper']);
447
	gen_disablealiaspopupdetail_field($section, $pconfig['disablealiaspopupdetail']);
448
	gen_pagenamefirst_field($section, $pconfig['pagenamefirst']);
449
}
450

    
451
/****f* pfsense-utils/gen_requirestatefilter_field
452
 * NAME
453
 *   gen_requirestatefilter_field
454
 * INPUTS
455
 *   Pointer to section object
456
 *   Initial value for the field
457
 * RESULT
458
 *   no return value, section object is updated
459
 ******/
460
function gen_requirestatefilter_field(&$section, $value) {
461
	$section->addInput(new Form_Checkbox(
462
		'requirestatefilter',
463
		'Require State Filter',
464
		'Do not display state table without a filter',
465
		$value
466
	))->setHelp('By default, the entire state table is displayed when entering '.
467
		'Diagnostics > States. This option requires a filter to be entered '.
468
		'before the states are displayed. Useful for systems with large state tables.');
469
}
470

    
471
/****f* pfsense-utils/gen_created_updated_fields
472
 * NAME
473
 *   gen_created_updated_fields
474
 * INPUTS
475
 *   Pointer to form object
476
 *   Array of created time and username
477
 *   Array of updated time and username
478
 * RESULT
479
 *   no return value, section object is added to form if needed
480
 ******/
481
function gen_created_updated_fields(&$form, $created, $updated, $tracker = 0) {
482
	$has_created_time = (isset($created['time']) && isset($created['username']));
483
	$has_updated_time = (isset($updated['time']) && isset($updated['username']));
484

    
485
	if ($has_created_time || $has_updated_time) {
486
		$section = new Form_Section('Rule Information');
487

    
488
		if (!empty($tracker)) {
489
			$section->addInput(new Form_StaticText(
490
				'Tracking ID',
491
				htmlspecialchars($tracker)
492
			));
493
		}
494

    
495
		if ($has_created_time) {
496
			$section->addInput(new Form_StaticText(
497
				'Created',
498
				htmlspecialchars(sprintf(
499
					gettext('%1$s by %2$s'),
500
					date(gettext("n/j/y H:i:s"), $created['time']),
501
					$created['username']))
502
			));
503
		}
504

    
505
		if ($has_updated_time) {
506
			$section->addInput(new Form_StaticText(
507
				'Updated',
508
				htmlspecialchars(sprintf(
509
					gettext('%1$s by %2$s'),
510
					date(gettext("n/j/y H:i:s"), $updated['time']),
511
					$updated['username']))
512
			));
513
		}
514

    
515
		$form->add($section);
516
	}
517
}
518

    
519
function hardware_offloading_applyflags($iface) {
520
	global $config;
521

    
522
	$flags_on = 0;
523
	$flags_off = 0;
524
	$options = pfSense_get_interface_addresses($iface);
525

    
526
	/* disable hardware checksum offloading for VirtIO network drivers,
527
	 * see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 */
528
	if (isset($config['system']['disablechecksumoffloading']) ||
529
	    stristr($iface, "vtnet") || stristr($iface, "ena")) { 
530
		if (isset($options['encaps']['txcsum'])) {
531
			$flags_off |= IFCAP_TXCSUM;
532
		}
533
		if (isset($options['encaps']['rxcsum'])) {
534
			$flags_off |= IFCAP_RXCSUM;
535
		}
536
		if (isset($options['encaps']['txcsum6'])) {
537
			$flags_off |= IFCAP_TXCSUM_IPV6;
538
		}
539
		if (isset($options['encaps']['rxcsum6'])) {
540
			$flags_off |= IFCAP_RXCSUM_IPV6;
541
		}
542
	} else {
543
		if (isset($options['caps']['txcsum'])) {
544
			$flags_on |= IFCAP_TXCSUM;
545
		}
546
		if (isset($options['caps']['rxcsum'])) {
547
			$flags_on |= IFCAP_RXCSUM;
548
		}
549
		if (isset($options['caps']['txcsum6'])) {
550
			$flags_on |= IFCAP_TXCSUM_IPV6;
551
		}
552
		if (isset($options['caps']['rxcsum6'])) {
553
			$flags_on |= IFCAP_RXCSUM_IPV6;
554
		}
555
	}
556

    
557
	if (isset($config['system']['disablesegmentationoffloading'])) {
558
		$flags_off |= IFCAP_TSO;
559
		$flags_off |= IFCAP_VLAN_HWTSO;
560
	} else if (isset($options['caps']['tso']) || isset($options['caps']['tso4']) || isset($options['caps']['tso6'])) {
561
		$flags_on |= IFCAP_TSO;
562
		$flags_on |= IFCAP_VLAN_HWTSO;
563
	}
564

    
565
	if (isset($config['system']['disablelargereceiveoffloading'])) {
566
		$flags_off |= IFCAP_LRO;
567
	} else if (isset($options['caps']['lro'])) {
568
		$flags_on |= IFCAP_LRO;
569
	}
570

    
571
	pfSense_interface_capabilities($iface, -$flags_off);
572
	pfSense_interface_capabilities($iface, $flags_on);
573
}
574

    
575
/****f* pfsense-utils/enable_hardware_offloading
576
 * NAME
577
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
578
 * INPUTS
579
 *   $interface	- string containing the physical interface to work on.
580
 * RESULT
581
 *   null
582
 * NOTES
583
 *   This function only supports the fxp driver's loadable microcode.
584
 ******/
585
function enable_hardware_offloading($interface) {
586
	global $g, $config;
587

    
588
	$int = get_real_interface($interface);
589
	if (empty($int)) {
590
		return;
591
	}
592

    
593
	if (!isset($config['system']['do_not_use_nic_microcode'])) {
594
		/* translate wan, lan, opt -> real interface if needed */
595
		$int_family = preg_split("/[0-9]+/", $int);
596
		$supported_ints = array('fxp');
597
		if (in_array($int_family, $supported_ints)) {
598
			if (does_interface_exist($int)) {
599
				pfSense_interface_flags($int, IFF_LINK0);
600
			}
601
		}
602
	}
603

    
604
	/* This is mostly for vlans and ppp types */
605
	$realhwif = get_parent_interface($interface);
606
	if ($realhwif[0] == $int) {
607
		hardware_offloading_applyflags($int);
608
	} else {
609
		hardware_offloading_applyflags($realhwif[0]);
610
		hardware_offloading_applyflags($int);
611
	}
612
}
613

    
614
/****f* pfsense-utils/is_alias_inuse
615
 * NAME
616
 *   checks to see if an alias is currently in use by a rule
617
 * INPUTS
618
 *
619
 * RESULT
620
 *   true or false
621
 * NOTES
622
 *
623
 ******/
624
function is_alias_inuse($alias) {
625
	global $g, $config;
626

    
627
	if ($alias == "") {
628
		return false;
629
	}
630
	/* loop through firewall rules looking for alias in use */
631
	if (is_array($config['filter']['rule'])) {
632
		foreach ($config['filter']['rule'] as $rule) {
633
			if ($rule['source']['address']) {
634
				if ($rule['source']['address'] == $alias) {
635
					return true;
636
				}
637
			}
638
			if ($rule['destination']['address']) {
639
				if ($rule['destination']['address'] == $alias) {
640
					return true;
641
				}
642
			}
643
		}
644
	}
645
	/* loop through nat rules looking for alias in use */
646
	if (is_array($config['nat']['rule'])) {
647
		foreach ($config['nat']['rule'] as $rule) {
648
			if ($rule['target'] && $rule['target'] == $alias) {
649
				return true;
650
			}
651
			if ($rule['source']['address'] && $rule['source']['address'] == $alias) {
652
				return true;
653
			}
654
			if ($rule['destination']['address'] && $rule['destination']['address'] == $alias) {
655
				return true;
656
			}
657
		}
658
	}
659
	return false;
660
}
661

    
662
/****f* pfsense-utils/is_schedule_inuse
663
 * NAME
664
 *   checks to see if a schedule is currently in use by a rule
665
 * INPUTS
666
 *
667
 * RESULT
668
 *   true or false
669
 * NOTES
670
 *
671
 ******/
672
function is_schedule_inuse($schedule) {
673
	global $g, $config;
674

    
675
	if ($schedule == "") {
676
		return false;
677
	}
678
	/* loop through firewall rules looking for schedule in use */
679
	if (is_array($config['filter']['rule'])) {
680
		foreach ($config['filter']['rule'] as $rule) {
681
			if ($rule['sched'] == $schedule) {
682
				return true;
683
			}
684
		}
685
	}
686
	return false;
687
}
688

    
689
/****f* pfsense-utils/setup_microcode
690
 * NAME
691
 *   enumerates all interfaces and calls enable_hardware_offloading which
692
 *   enables a NIC's supported hardware features.
693
 * INPUTS
694
 *
695
 * RESULT
696
 *   null
697
 * NOTES
698
 *   This function only supports the fxp driver's loadable microcode.
699
 ******/
700
function setup_microcode() {
701

    
702
	/* if list */
703
	$iflist = get_configured_interface_list(true);
704
	foreach ($iflist as $if => $ifdescr) {
705
		enable_hardware_offloading($if);
706
	}
707
	unset($iflist);
708
}
709

    
710
/****f* pfsense-utils/get_carp_status
711
 * NAME
712
 *   get_carp_status - Return whether CARP is enabled or disabled.
713
 * RESULT
714
 *   boolean	- true if CARP is enabled, false if otherwise.
715
 ******/
716
function get_carp_status() {
717
	/* grab the current status of carp */
718
	$status = get_single_sysctl('net.inet.carp.allow');
719
	return (intval($status) > 0);
720
}
721

    
722
/*
723
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
724

    
725
 */
726
function convert_ip_to_network_format($ip, $subnet) {
727
	$ipsplit = explode('.', $ip);
728
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
729
	return $string;
730
}
731

    
732
/*
733
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
734
 */
735
function get_carp_interface_status($carpid) {
736

    
737
	$carpiface = get_configured_vip_interface($carpid);
738
	if ($carpiface == NULL)
739
		return "";
740
	$interface = get_real_interface($carpiface);
741
	if ($interface == NULL)
742
		return "";
743
	$vip = get_configured_vip($carpid);
744
	if ($vip == NULL || !isset($vip['vhid']))
745
		return "";
746

    
747
	$vhid = $vip['vhid'];
748
	$carp_query = '';
749
	$_gb = exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
750
	foreach ($carp_query as $int) {
751
		if (stripos($int, "MASTER"))
752
			return "MASTER";
753
		elseif (stripos($int, "BACKUP"))
754
			return "BACKUP";
755
		elseif (stripos($int, "INIT"))
756
			return "INIT";
757
	}
758

    
759
	return "";
760
}
761

    
762
/*
763
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
764
 * This function return false if CARP is not enabled on any interface of the captive portal zone
765
 */
766
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
767
	global $config;
768

    
769
	$cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
770

    
771
	if (is_array($config['virtualip']['vip'])) {
772
		foreach ($cpinterfaces as $interface) {
773
			foreach ($config['virtualip']['vip'] as $vip) {
774
				if (($vip['interface'] == $interface) && ($vip['mode'] == "carp")) {
775
					if (get_carp_interface_status("_vip{$vip['uniqid']}") != "MASTER") {
776
						return true;
777
					}
778
				}
779
			}
780
		}
781
	}
782
	return false;
783
}
784

    
785
/*
786
 * get_pfsync_interface_status($pfsyncinterface): returns the status of a pfsync
787
 */
788
function get_pfsync_interface_status($pfsyncinterface) {
789
	if (!does_interface_exist($pfsyncinterface)) {
790
		return;
791
	}
792

    
793
	return exec_command("/sbin/ifconfig {$pfsyncinterface} | /usr/bin/awk '/pfsync:/ {print \$5}'");
794
}
795

    
796
/*
797
 * add_rule_to_anchor($anchor, $rule): adds the specified rule to an anchor
798
 */
799
function add_rule_to_anchor($anchor, $rule, $label) {
800
	mwexec("echo " . escapeshellarg($rule) . " | /sbin/pfctl -a " . escapeshellarg($anchor) . ":" . escapeshellarg($label) . " -f -");
801
}
802

    
803
/*
804
 * remove_text_from_file
805
 * remove $text from file $file
806
 */
807
function remove_text_from_file($file, $text) {
808
	if (!file_exists($file) && !is_writable($file)) {
809
		return;
810
	}
811
	$filecontents = file_get_contents($file);
812
	$text = str_replace($text, "", $filecontents);
813
	@file_put_contents($file, $text);
814
}
815

    
816
/*
817
 *   after_sync_bump_adv_skew(): create skew values by 1S
818
 */
819
function after_sync_bump_adv_skew() {
820
	global $config, $g;
821
	$processed_skew = 1;
822
	init_config_arr(array('virtualip', 'vip'));
823
	$a_vip = &$config['virtualip']['vip'];
824
	foreach ($a_vip as $vipent) {
825
		if ($vipent['advskew'] <> "") {
826
			$processed_skew = 1;
827
			$vipent['advskew'] = $vipent['advskew']+1;
828
		}
829
	}
830
	if ($processed_skew == 1) {
831
		write_config(gettext("After synch increase advertising skew"));
832
	}
833
}
834

    
835
/*
836
 * get_filename_from_url($url): converts a url to its filename.
837
 */
838
function get_filename_from_url($url) {
839
	return basename($url);
840
}
841

    
842
/*
843
 *   get_dir: return an array of $dir
844
 */
845
function get_dir($dir) {
846
	$dir_array = array();
847
	$d = dir($dir);
848
	if (!is_object($d)) {
849
		return array();
850
	}
851
	while (false !== ($entry = $d->read())) {
852
		array_push($dir_array, $entry);
853
	}
854
	$d->close();
855
	return $dir_array;
856
}
857

    
858
/****f* pfsense-utils/WakeOnLan
859
 * NAME
860
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
861
 * RESULT
862
 *   true/false - true if the operation was successful
863
 ******/
864
function WakeOnLan($addr, $mac) {
865
	$addr_byte = explode(':', $mac);
866
	$hw_addr = '';
867

    
868
	for ($a = 0; $a < 6; $a++) {
869
		$hw_addr .= chr(hexdec($addr_byte[$a]));
870
	}
871

    
872
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
873

    
874
	for ($a = 1; $a <= 16; $a++) {
875
		$msg .= $hw_addr;
876
	}
877

    
878
	// send it to the broadcast address using UDP
879
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
880
	if ($s == false) {
881
		log_error(gettext("Error creating socket!"));
882
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error($s), socket_strerror(socket_last_error($s))));
883
	} else {
884
		// setting a broadcast option to socket:
885
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
886
		if ($opt_ret < 0) {
887
			log_error(sprintf(gettext("setsockopt() failed, error: %s"), strerror($opt_ret)));
888
		}
889
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
890
		socket_close($s);
891
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
892
		return true;
893
	}
894

    
895
	return false;
896
}
897

    
898
/*
899
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
900
 *					 Useful for finding paths and stripping file extensions.
901
 */
902
function reverse_strrchr($haystack, $needle) {
903
	if (!is_string($haystack)) {
904
		return;
905
	}
906
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
907
}
908

    
909
/*
910
 *  backup_config_section($section): returns as an xml file string of
911
 *                                   the configuration section
912
 */
913
function backup_config_section($section_name) {
914
	global $config;
915
	$new_section = &$config[$section_name];
916
	/* generate configuration XML */
917
	$xmlconfig = dump_xml_config($new_section, $section_name);
918
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
919
	return $xmlconfig;
920
}
921

    
922
/*
923
 *  restore_config_section($section_name, new_contents): restore a configuration section,
924
 *                                                  and write the configuration out
925
 *                                                  to disk/cf.
926
 */
927
function restore_config_section($section_name, $new_contents) {
928
	global $config, $g;
929
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
930
	fwrite($fout, $new_contents);
931
	fclose($fout);
932

    
933
	$xml = parse_xml_config($g['tmp_path'] . "/tmpxml", null);
934
	if ($xml['pfsense']) {
935
		$xml = $xml['pfsense'];
936
	}
937
	if ($xml[$section_name]) {
938
		$section_xml = $xml[$section_name];
939
	} else {
940
		$section_xml = -1;
941
	}
942

    
943
	@unlink($g['tmp_path'] . "/tmpxml");
944
	if ($section_xml === -1) {
945
		return false;
946
	}
947

    
948
	/* Save current pkg repo to re-add on new config */
949
	unset($pkg_repo_conf_path);
950
	if ($section_name == "system" &&
951
	    isset($config['system']['pkg_repo_conf_path'])) {
952
		$pkg_repo_conf_path = $config['system']['pkg_repo_conf_path'];
953
	}
954

    
955
	$config[$section_name] = &$section_xml;
956
	if (file_exists("{$g['tmp_path']}/config.cache")) {
957
		unlink("{$g['tmp_path']}/config.cache");
958
	}
959

    
960
	/* Restore previously pkg repo configured */
961
	if ($section_name == "system") {
962
		if (isset($pkg_repo_conf_path)) {
963
			$config['system']['pkg_repo_conf_path'] =
964
			    $pkg_repo_conf_path;
965
		} elseif (isset($config['system']['pkg_repo_conf_path'])) {
966
			unset($config['system']['pkg_repo_conf_path']);
967
		}
968
	}
969

    
970
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
971
	disable_security_checks();
972
	return true;
973
}
974

    
975
/*
976
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
977
 *                                                  and write the configuration out
978
 *                                                  to disk/cf.  But preserve the prior
979
 * 													structure if needed
980
 */
981
function merge_config_section($section_name, $new_contents) {
982
	global $config;
983
	$fname = get_tmp_filename();
984
	$fout = fopen($fname, "w");
985
	fwrite($fout, $new_contents);
986
	fclose($fout);
987
	$section_xml = parse_xml_config($fname, $section_name);
988
	$config[$section_name] = $section_xml;
989
	unlink($fname);
990
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
991
	disable_security_checks();
992
	return;
993
}
994

    
995
/*
996
 * rmdir_recursive($path, $follow_links=false)
997
 * Recursively remove a directory tree (rm -rf path)
998
 * This is for directories _only_
999
 */
1000
function rmdir_recursive($path, $follow_links=false) {
1001
	$to_do = glob($path);
1002
	if (!is_array($to_do)) {
1003
		$to_do = array($to_do);
1004
	}
1005
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
1006
		if (file_exists($workingdir)) {
1007
			if (is_dir($workingdir)) {
1008
				$dir = opendir($workingdir);
1009
				while ($entry = readdir($dir)) {
1010
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
1011
						unlink("$workingdir/$entry");
1012
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
1013
						rmdir_recursive("$workingdir/$entry");
1014
					}
1015
				}
1016
				closedir($dir);
1017
				rmdir($workingdir);
1018
			} elseif (is_file($workingdir)) {
1019
				unlink($workingdir);
1020
			}
1021
		}
1022
	}
1023
	return;
1024
}
1025

    
1026
/*
1027
 * host_firmware_version(): Return the versions used in this install
1028
 */
1029
function host_firmware_version($tocheck = "") {
1030
	global $g, $config;
1031

    
1032
	$os_version = trim(substr(php_uname("r"), 0, strpos(php_uname("r"), '-')));
1033

    
1034
	return array(
1035
		"firmware" => array("version" => $g['product_version']),
1036
		"kernel"   => array("version" => $os_version),
1037
		"base"     => array("version" => $os_version),
1038
		"platform" => $g['product_label'],
1039
		"config_version" => $config['version']
1040
	);
1041
}
1042

    
1043
function get_disk_info() {
1044
	$diskout = "";
1045
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
1046
	return explode(' ', $diskout[0]);
1047
}
1048

    
1049
/****f* pfsense-utils/strncpy
1050
 * NAME
1051
 *   strncpy - copy strings
1052
 * INPUTS
1053
 *   &$dst, $src, $length
1054
 * RESULT
1055
 *   none
1056
 ******/
1057
function strncpy(&$dst, $src, $length) {
1058
	if (strlen($src) > $length) {
1059
		$dst = substr($src, 0, $length);
1060
	} else {
1061
		$dst = $src;
1062
	}
1063
}
1064

    
1065
/****f* pfsense-utils/reload_interfaces_sync
1066
 * NAME
1067
 *   reload_interfaces - reload all interfaces
1068
 * INPUTS
1069
 *   none
1070
 * RESULT
1071
 *   none
1072
 ******/
1073
function reload_interfaces_sync() {
1074
	global $config, $g;
1075

    
1076
	if ($g['debug']) {
1077
		log_error(gettext("reload_interfaces_sync() is starting."));
1078
	}
1079

    
1080
	/* parse config.xml again */
1081
	$config = parse_config(true);
1082

    
1083
	/* enable routing */
1084
	system_routing_enable();
1085
	if ($g['debug']) {
1086
		log_error(gettext("Enabling system routing"));
1087
	}
1088

    
1089
	if ($g['debug']) {
1090
		log_error(gettext("Cleaning up Interfaces"));
1091
	}
1092

    
1093
	/* set up interfaces */
1094
	interfaces_configure();
1095
}
1096

    
1097
/****f* pfsense-utils/reload_all
1098
 * NAME
1099
 *   reload_all - triggers a reload of all settings
1100
 *   * INPUTS
1101
 *   none
1102
 * RESULT
1103
 *   none
1104
 ******/
1105
function reload_all() {
1106
	send_event("service reload all");
1107
}
1108

    
1109
/****f* pfsense-utils/reload_interfaces
1110
 * NAME
1111
 *   reload_interfaces - triggers a reload of all interfaces
1112
 * INPUTS
1113
 *   none
1114
 * RESULT
1115
 *   none
1116
 ******/
1117
function reload_interfaces() {
1118
	send_event("interface all reload");
1119
}
1120

    
1121
/****f* pfsense-utils/reload_all_sync
1122
 * NAME
1123
 *   reload_all - reload all settings
1124
 *   * INPUTS
1125
 *   none
1126
 * RESULT
1127
 *   none
1128
 ******/
1129
function reload_all_sync() {
1130
	global $config, $g;
1131

    
1132
	/* parse config.xml again */
1133
	$config = parse_config(true);
1134

    
1135
	/* set up our timezone */
1136
	system_timezone_configure();
1137

    
1138
	/* set up our hostname */
1139
	system_hostname_configure();
1140

    
1141
	/* make hosts file */
1142
	system_hosts_generate();
1143

    
1144
	/* generate resolv.conf */
1145
	system_resolvconf_generate();
1146

    
1147
	/* enable routing */
1148
	system_routing_enable();
1149

    
1150
	/* set up interfaces */
1151
	interfaces_configure();
1152

    
1153
	/* start dyndns service */
1154
	services_dyndns_configure();
1155

    
1156
	/* configure cron service */
1157
	configure_cron();
1158

    
1159
	/* start the NTP client */
1160
	system_ntp_configure();
1161

    
1162
	/* sync pw database */
1163
	unlink_if_exists("/etc/spwd.db.tmp");
1164
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1165

    
1166
	/* restart sshd */
1167
	send_event("service restart sshd");
1168

    
1169
	/* restart webConfigurator if needed */
1170
	send_event("service restart webgui");
1171
}
1172

    
1173
function load_loader_conf($loader_conf = NULL, $local = false) {
1174

    
1175
	if ($loader_conf == NULL) {
1176
		return (NULL);
1177
	}
1178
	if (file_exists($loader_conf)) {
1179
		$input = file_get_contents($loader_conf);
1180
	} else {
1181
		$input = "";
1182
	}
1183

    
1184
	$input_split = explode("\n", $input);
1185

    
1186
	/*
1187
	 * Loop through and only add lines that are not empty and not
1188
	 * managed by us.
1189
	 */
1190
	$data = array();
1191
	/* These values should be removed from loader.conf and loader.conf.local
1192
	 * As they will be replaced when necessary. */
1193
	$remove = array("hw.usb.no_pf", "hint.mdio.0.at", "hint.e6000sw.0",
1194
	    "hw.e6000sw.default_disabled", "vm.pmap.pti",
1195
	    "net.pf.request_maxcount", "hw.hn.vf_transparent",
1196
	    "hw.hn.use_if_start");
1197
	if (!$local) {
1198
		/* These values should only be filtered in loader.conf, not .local */
1199
		$remove = array_merge($remove,
1200
		    array("autoboot_delay", "console", "comconsole_speed",
1201
		    "boot_multicons", "boot_serial", "hint.uart.0.flags",
1202
		    "hint.uart.1.flags"));
1203
	}
1204
	foreach ($input_split as $line) {
1205
		if (empty($line)) {
1206
			continue;
1207
		}
1208
		$skip = false;
1209
		list($name, $value) = explode('=', $line, 2);
1210
		foreach($remove as $rid => $rkey) {
1211
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1212
				$skip = true;
1213
				break;
1214
			}
1215
		}
1216
		if (!$skip) {
1217
			$data[] = $line;
1218
		}
1219
	}
1220

    
1221
	return ($data);
1222
}
1223

    
1224
function setup_loader_settings($path = "", $upgrade = false) {
1225
	global $g, $config;
1226

    
1227
	$boot_config_file = "{$path}/boot.config";
1228
	$loader_conf_file = "{$path}/boot/loader.conf";
1229

    
1230
	$serialspeed = (is_numeric($config['system']['serialspeed'])) ? $config['system']['serialspeed'] : "115200";
1231

    
1232
	$vga_only = false;
1233
	$serial_only = false;
1234
	$specific_platform = system_identify_specific_platform();
1235
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1236
	if ($specific_platform['name'] == 'XG-1540') {
1237
		$vga_only = true;
1238
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1239
		$g['primaryconsole_force'] = "video";
1240
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1241
	    $specific_platform['name'] == 'RCC' ||
1242
	    $specific_platform['name'] == 'SG-2220' ||
1243
	    $specific_platform['name'] == 'apu2') {
1244
		$serial_only = true;
1245
	}
1246

    
1247
	/* Serial console - write out /boot.config */
1248
	if (file_exists($boot_config_file)) {
1249
		$boot_config = file_get_contents($boot_config_file);
1250
	} else {
1251
		$boot_config = "";
1252
	}
1253
	$boot_config_split = explode("\n", $boot_config);
1254
	$data = array();
1255
	foreach ($boot_config_split as $bcs) {
1256
		/* Ignore -D and -h lines now */
1257
		if (!empty($bcs) && !stristr($bcs, "-D") &&
1258
		    !stristr($bcs, "-h")) {
1259
			$data[] = $bcs;
1260
		}
1261
	}
1262
	if ($serial_only === true) {
1263
		$data[] = "-S{$serialspeed} -h";
1264
	} elseif (is_serial_enabled()) {
1265
		$data[] = "-S{$serialspeed} -D";
1266
	}
1267

    
1268
	if (empty($data)) {
1269
		@unlink($boot_conf_file);
1270
	} else {
1271
		safe_write_file($boot_config_file, $data);
1272
	}
1273
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1274

    
1275
	/* Serial console - write out /boot/loader.conf */
1276
	if ($upgrade) {
1277
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1278
	}
1279

    
1280
	$data = load_loader_conf($loader_conf_file, false);
1281
	if ($serial_only === true) {
1282
		$data[] = 'boot_serial="YES"';
1283
		$data[] = 'console="comconsole"';
1284
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1285
	} elseif ($vga_only === true) {
1286
		$data[] = "console=\"{$video_console_type}\"";
1287
	} elseif (is_serial_enabled()) {
1288
		$data[] = 'boot_multicons="YES"';
1289
		$data[] = 'boot_serial="YES"';
1290
		$primaryconsole = isset($g['primaryconsole_force']) ?
1291
		    $g['primaryconsole_force'] :
1292
		    $config['system']['primaryconsole'];
1293
		switch ($primaryconsole) {
1294
			case "video":
1295
				$data[] = "console=\"{$video_console_type},comconsole\"";
1296
				break;
1297
			case "serial":
1298
			default:
1299
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1300
		}
1301
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1302
	}
1303

    
1304
	if ($specific_platform['name'] == 'RCC-VE' ||
1305
	    $specific_platform['name'] == 'RCC' ||
1306
	    $specific_platform['name'] == 'SG-2220') {
1307
		$data[] = 'comconsole_port="0x2F8"';
1308
		$data[] = 'hint.uart.0.flags="0x00"';
1309
		$data[] = 'hint.uart.1.flags="0x10"';
1310
	}
1311
	$data[] = 'autoboot_delay="3"';
1312
	if (isset($config['system']['pti_disabled'])) {
1313
		$data[] = 'vm.pmap.pti="0"';
1314
	}
1315

    
1316
	/* Enable ALTQ support for hnX NICs. */
1317
	if (isset($config['system']['hn_altq_enable'])) {
1318
		$data[] = 'hw.hn.vf_transparent="0"';
1319
		$data[] = 'hw.hn.use_if_start="1"';
1320
	}
1321

    
1322
	safe_write_file($loader_conf_file, $data);
1323

    
1324
	/* Filter loader.conf.local to avoid duplicate settings. */
1325
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1326
	$data = load_loader_conf($loader_conf_file, true);
1327
	if (empty($data)) {
1328
		@unlink($loader_conf_file);
1329
	} else {
1330
		safe_write_file($loader_conf_file, $data);
1331
	}
1332

    
1333
}
1334

    
1335
function console_configure($when = "save", $path = "") {
1336
	global $config;
1337
	$ttys_file = "{$path}/etc/ttys";
1338

    
1339
	/* Update the loader settings. */
1340
	setup_loader_settings($path, ($when == "upgrade"));
1341

    
1342
	$ttys = file_get_contents($ttys_file);
1343
	$ttys_split = explode("\n", $ttys);
1344

    
1345
	$data = array();
1346

    
1347
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1348

    
1349
	if (isset($config['system']['disableconsolemenu'])) {
1350
		$console_type = 'Pc';
1351
		$serial_type = '3wire';
1352
	} else {
1353
		$console_type = 'al.Pc';
1354
		$serial_type = 'al.3wire';
1355
	}
1356

    
1357
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1358
	$virt_line =
1359
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1360
	$ttyu_line =
1361
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1362

    
1363
	$found = array();
1364

    
1365
	foreach ($ttys_split as $tty) {
1366
		/* Ignore blank lines */
1367
		if (empty($tty)) {
1368
			continue;
1369
		}
1370

    
1371
		if (stristr($tty, "ttyv0")) {
1372
			$found['ttyv0'] = 1;
1373
			$data[] = "ttyv0\t{$virt_line}";
1374
		} elseif (stristr($tty, "xc0")) {
1375
			$found['xc0'] = 1;
1376
			$data[] = "xc0\t{$virt_line}";
1377
		} elseif (stristr($tty, "ttyu")) {
1378
			$ttyn = substr($tty, 0, 5);
1379
			$found[$ttyn] = 1;
1380
			$data[] = "{$ttyn}\t{$ttyu_line}";
1381
		} elseif (substr($tty, 0, 7) == 'console') {
1382
			$found['console'] = 1;
1383
			$data[] = $tty;
1384
		} else {
1385
			$data[] = $tty;
1386
		}
1387
	}
1388
	unset($on_off, $console_type, $serial_type);
1389

    
1390
	/* Detect missing main lines on original file and try to rebuild it */
1391
	$items = array(
1392
		'console',
1393
		'ttyv0',
1394
		'ttyu0',
1395
		'ttyu1',
1396
		'ttyu2',
1397
		'ttyu3',
1398
		'xc0'
1399
	);
1400

    
1401
	foreach ($items as $item) {
1402
		if (isset($found[$item])) {
1403
			continue;
1404
		}
1405

    
1406
		if ($item == 'console') {
1407
			$data[] = $console_line;
1408
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1409
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1410
			$data[] = "{$item}\t{$virt_line}";
1411
		} else {
1412
			$data[] = "{$item}\t{$ttyu_line}";
1413
		}
1414
	}
1415

    
1416
	safe_write_file($ttys_file, $data);
1417

    
1418
	unset($ttys, $ttys_file, $ttys_split, $data);
1419

    
1420
	if ($when != "upgrade") {
1421
		reload_ttys();
1422
	}
1423

    
1424
	return;
1425
}
1426

    
1427
function is_serial_enabled() {
1428
	global $g, $config;
1429

    
1430
	if (!isset($g['enableserial_force']) &&
1431
	    !isset($config['system']['enableserial'])) {
1432
		return false;
1433
	}
1434

    
1435
	return true;
1436
}
1437

    
1438
function reload_ttys() {
1439
	// Send a HUP signal to init will make it reload /etc/ttys
1440
	posix_kill(1, SIGHUP);
1441
}
1442

    
1443
function print_value_list($list, $count = 10, $separator = ",") {
1444
	$list = implode($separator, array_slice($list, 0, $count));
1445
	if (count($list) < $count) {
1446
		$list .= ".";
1447
	} else {
1448
		$list .= "...";
1449
	}
1450
	return $list;
1451
}
1452

    
1453
/* DHCP enabled on any interfaces? */
1454
function is_dhcp_server_enabled() {
1455
	global $config;
1456

    
1457
	if (!is_array($config['dhcpd'])) {
1458
		return false;
1459
	}
1460

    
1461
	foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
1462
		if (isset($dhcpifconf['enable']) && !empty($config['interfaces'][$dhcpif])) {
1463
			return true;
1464
		}
1465
	}
1466

    
1467
	return false;
1468
}
1469

    
1470
/* DHCP enabled on any interfaces? */
1471
function is_dhcpv6_server_enabled() {
1472
	global $config;
1473

    
1474
	if (is_array($config['interfaces'])) {
1475
		foreach ($config['interfaces'] as $ifcfg) {
1476
			if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1477
				return true;
1478
			}
1479
		}
1480
	}
1481

    
1482
	if (!is_array($config['dhcpdv6'])) {
1483
		return false;
1484
	}
1485

    
1486
	foreach ($config['dhcpdv6'] as $dhcpv6if => $dhcpv6ifconf) {
1487
		if (isset($dhcpv6ifconf['enable']) && !empty($config['interfaces'][$dhcpv6if])) {
1488
			return true;
1489
		}
1490
	}
1491

    
1492
	return false;
1493
}
1494

    
1495
/* radvd enabled on any interfaces? */
1496
function is_radvd_enabled() {
1497
	global $config;
1498

    
1499
	if (!is_array($config['dhcpdv6'])) {
1500
		$config['dhcpdv6'] = array();
1501
	}
1502

    
1503
	$dhcpdv6cfg = $config['dhcpdv6'];
1504
	$Iflist = get_configured_interface_list();
1505

    
1506
	/* handle manually configured DHCP6 server settings first */
1507
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1508
		if (!isset($config['interfaces'][$dhcpv6if]['enable'])) {
1509
			continue;
1510
		}
1511

    
1512
		if (!isset($dhcpv6ifconf['ramode'])) {
1513
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1514
		}
1515

    
1516
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1517
			continue;
1518
		}
1519

    
1520
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1521
		if (!is_ipaddrv6($ifcfgipv6)) {
1522
			continue;
1523
		}
1524

    
1525
		return true;
1526
	}
1527

    
1528
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1529
	foreach ($Iflist as $if => $ifdescr) {
1530
		if (!isset($config['interfaces'][$if]['track6-interface'])) {
1531
			continue;
1532
		}
1533
		if (!isset($config['interfaces'][$if]['enable'])) {
1534
			continue;
1535
		}
1536

    
1537
		$ifcfgipv6 = get_interface_ipv6($if);
1538
		if (!is_ipaddrv6($ifcfgipv6)) {
1539
			continue;
1540
		}
1541

    
1542
		$ifcfgsnv6 = get_interface_subnetv6($if);
1543
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1544

    
1545
		if (!is_ipaddrv6($subnetv6)) {
1546
			continue;
1547
		}
1548

    
1549
		return true;
1550
	}
1551

    
1552
	return false;
1553
}
1554

    
1555
/* Any PPPoE servers enabled? */
1556
function is_pppoe_server_enabled() {
1557
	global $config;
1558

    
1559
	$pppoeenable = false;
1560

    
1561
	if (!is_array($config['pppoes']) || !is_array($config['pppoes']['pppoe'])) {
1562
		return false;
1563
	}
1564

    
1565
	foreach ($config['pppoes']['pppoe'] as $pppoes) {
1566
		if ($pppoes['mode'] == 'server') {
1567
			$pppoeenable = true;
1568
		}
1569
	}
1570

    
1571
	return $pppoeenable;
1572
}
1573

    
1574
/* Optional arg forces hh:mm:ss without days */
1575
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1576
	if (!is_numericint($sec)) {
1577
		return '-';
1578
	}
1579
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1580
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1581
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1582
					(int)(($sec % 3600)/60),
1583
					$sec % 60
1584
				);
1585
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1586
}
1587

    
1588
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1589

    
1590
function get_ppp_uptime($port) {
1591
	if (file_exists("/conf/{$port}.log")) {
1592
		$saved_time = file_get_contents("/conf/{$port}.log");
1593
		$uptime_data = explode("\n", $saved_time);
1594
		$sec = 0;
1595
		foreach ($uptime_data as $upt) {
1596
			$sec += substr($upt, 1 + strpos($upt, " "));
1597
		}
1598
		return convert_seconds_to_dhms($sec);
1599
	} else {
1600
		$total_time = gettext("No history data found!");
1601
		return $total_time;
1602
	}
1603
}
1604

    
1605
//returns interface information
1606
function get_interface_info($ifdescr) {
1607
	global $config, $g;
1608

    
1609
	$ifinfo = array();
1610
	if (empty($config['interfaces'][$ifdescr])) {
1611
		return;
1612
	}
1613
	$ifinfo['hwif'] = $config['interfaces'][$ifdescr]['if'];
1614
	$ifinfo['enable'] = isset($config['interfaces'][$ifdescr]['enable']);
1615
	$ifinfo['if'] = get_real_interface($ifdescr);
1616

    
1617
	$chkif = $ifinfo['if'];
1618
	$ifinfotmp = pfSense_get_interface_addresses($chkif);
1619
	$ifinfo['status'] = $ifinfotmp['status'];
1620
	if (empty($ifinfo['status'])) {
1621
		$ifinfo['status'] = "down";
1622
	}
1623
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1624
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1625
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1626
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1627
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1628
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1629
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1630
	if (isset($ifinfotmp['link0'])) {
1631
		$link0 = "down";
1632
	}
1633
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1634
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1635
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1636
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1637
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1638
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1639

    
1640
	/* Use pfctl for non wrapping 64 bit counters */
1641
	/* Pass */
1642
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1643
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1644
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1645
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1646
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1647
	$in4_pass = $pf_in4_pass[5];
1648
	$out4_pass = $pf_out4_pass[5];
1649
	$in4_pass_packets = $pf_in4_pass[3];
1650
	$out4_pass_packets = $pf_out4_pass[3];
1651
	$in6_pass = $pf_in6_pass[5];
1652
	$out6_pass = $pf_out6_pass[5];
1653
	$in6_pass_packets = $pf_in6_pass[3];
1654
	$out6_pass_packets = $pf_out6_pass[3];
1655
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1656
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1657
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1658
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1659

    
1660
	/* Block */
1661
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1662
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1663
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1664
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1665
	$in4_block = $pf_in4_block[5];
1666
	$out4_block = $pf_out4_block[5];
1667
	$in4_block_packets = $pf_in4_block[3];
1668
	$out4_block_packets = $pf_out4_block[3];
1669
	$in6_block = $pf_in6_block[5];
1670
	$out6_block = $pf_out6_block[5];
1671
	$in6_block_packets = $pf_in6_block[3];
1672
	$out6_block_packets = $pf_out6_block[3];
1673
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1674
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1675
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1676
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1677

    
1678
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1679
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1680
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1681
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1682

    
1683
	$ifconfiginfo = "";
1684
	$link_type = $config['interfaces'][$ifdescr]['ipaddr'];
1685
	switch ($link_type) {
1686
		/* DHCP? -> see if dhclient is up */
1687
		case "dhcp":
1688
			/* see if dhclient is up */
1689
			if (find_dhclient_process($ifinfo['if']) != 0) {
1690
				$ifinfo['dhcplink'] = "up";
1691
			} else {
1692
				$ifinfo['dhcplink'] = "down";
1693
			}
1694

    
1695
			break;
1696
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1697
		case "pppoe":
1698
		case "pptp":
1699
		case "l2tp":
1700
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1701
				/* get PPPoE link status for dial on demand */
1702
				$ifinfo["{$link_type}link"] = "up";
1703
			} else {
1704
				$ifinfo["{$link_type}link"] = "down";
1705
			}
1706

    
1707
			break;
1708
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1709
		case "ppp":
1710
			if ($ifinfo['status'] == "up") {
1711
				$ifinfo['ppplink'] = "up";
1712
			} else {
1713
				$ifinfo['ppplink'] = "down" ;
1714
			}
1715

    
1716
			if (empty($ifinfo['status'])) {
1717
				$ifinfo['status'] = "down";
1718
			}
1719

    
1720
			if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) {
1721
				foreach ($config['ppps']['ppp'] as $pppid => $ppp) {
1722
					if ($config['interfaces'][$ifdescr]['if'] == $ppp['if']) {
1723
						break;
1724
					}
1725
				}
1726
			}
1727
			$dev = $ppp['ports'];
1728
			if ($config['interfaces'][$ifdescr]['if'] != $ppp['if'] || empty($dev)) {
1729
				break;
1730
			}
1731
			if (!file_exists($dev)) {
1732
				$ifinfo['nodevice'] = 1;
1733
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1734
			}
1735

    
1736
			$usbmodemoutput = array();
1737
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1738
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1739
			if (file_exists($mondev)) {
1740
				$cellstats = file($mondev);
1741
				/* skip header */
1742
				$a_cellstats = explode(",", $cellstats[1]);
1743
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1744
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1745
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1746
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1747
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1748
				}
1749
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1750
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1751
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1752
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1753
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1754
				}
1755
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1756
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1757
				$ifinfo['cell_sent'] = $a_cellstats[6];
1758
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1759
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1760
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1761
			}
1762
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1763
			if (isset($ppp['uptime'])) {
1764
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1765
			}
1766
			break;
1767
		default:
1768
			break;
1769
	}
1770

    
1771
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1772
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1773
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1774
	}
1775

    
1776
	if ($ifinfo['status'] == "up") {
1777
		/* try to determine media with ifconfig */
1778
		unset($ifconfiginfo);
1779
		exec("/sbin/ifconfig " . $ifinfo['if'], $ifconfiginfo);
1780
		$wifconfiginfo = array();
1781
		if (is_interface_wireless($ifdescr)) {
1782
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1783
			array_shift($wifconfiginfo);
1784
		}
1785
		$matches = "";
1786
		foreach ($ifconfiginfo as $ici) {
1787

    
1788
			/* don't list media/speed for wireless cards, as it always
1789
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1790
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1791
				$ifinfo['media'] = $matches[1];
1792
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1793
				$ifinfo['media'] = $matches[1];
1794
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1795
				$ifinfo['media'] = $matches[1];
1796
			}
1797

    
1798
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1799
				if ($matches[1] != "active") {
1800
					$ifinfo['status'] = $matches[1];
1801
				}
1802
				if ($ifinfo['status'] == gettext("running")) {
1803
					$ifinfo['status'] = gettext("up");
1804
				}
1805
			}
1806
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1807
				$ifinfo['channel'] = $matches[1];
1808
			}
1809
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1810
				if ($matches[1][0] == '"') {
1811
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1812
				}
1813
				else {
1814
					$ifinfo['ssid'] = $matches[1];
1815
				}
1816
			}
1817
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1818
				$ifinfo['laggproto'] = $matches[1];
1819
			}
1820
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1821
				$ifinfo['laggport'][] = $matches[1];
1822
			}
1823
		}
1824
		foreach ($wifconfiginfo as $ici) {
1825
			$elements = preg_split("/[ ]+/i", $ici);
1826
			if ($elements[0] != "") {
1827
				$ifinfo['bssid'] = $elements[0];
1828
			}
1829
			if ($elements[3] != "") {
1830
				$ifinfo['rate'] = $elements[3];
1831
			}
1832
			if ($elements[4] != "") {
1833
				$ifinfo['rssi'] = $elements[4];
1834
			}
1835
		}
1836
		/* lookup the gateway */
1837
		if (interface_has_gateway($ifdescr)) {
1838
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1839
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1840
		}
1841
	}
1842

    
1843
	$bridge = "";
1844
	$bridge = link_interface_to_bridge($ifdescr);
1845
	if ($bridge) {
1846
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1847
		if (stristr($bridge_text, "blocking") <> false) {
1848
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1849
			$ifinfo['bridgeint'] = $bridge;
1850
		} else if (stristr($bridge_text, "learning") <> false) {
1851
			$ifinfo['bridge'] = gettext("learning");
1852
			$ifinfo['bridgeint'] = $bridge;
1853
		} else if (stristr($bridge_text, "forwarding") <> false) {
1854
			$ifinfo['bridge'] = gettext("forwarding");
1855
			$ifinfo['bridgeint'] = $bridge;
1856
		}
1857
	}
1858

    
1859
	return $ifinfo;
1860
}
1861

    
1862
//returns cpu speed of processor. Good for determining capabilities of machine
1863
function get_cpu_speed() {
1864
	return get_single_sysctl("hw.clockrate");
1865
}
1866

    
1867
function get_uptime_sec() {
1868
	$boottime = "";
1869
	$matches = "";
1870
	$boottime = get_single_sysctl("kern.boottime");
1871
	preg_match("/sec = (\d+)/", $boottime, $matches);
1872
	$boottime = $matches[1];
1873
	if (intval($boottime) == 0) {
1874
		return 0;
1875
	}
1876

    
1877
	$uptime = time() - $boottime;
1878
	return $uptime;
1879
}
1880

    
1881
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1882
	$dnsresult = array();
1883
	$resolved = array();
1884
	$errreporting = error_reporting();
1885
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1886
	foreach ($recordtypes as $recordtype) {
1887
		$tmp = dns_get_record($host, $recordtype);
1888
		if (is_array($tmp)) {
1889
			$dnsresult = array_merge($dnsresult, $tmp);
1890
		}
1891
	}
1892
	error_reporting($errreporting);// restore original php warning/error settings.
1893
	foreach ($dnsresult as $item) {
1894
		$newitem = array();
1895
		$newitem['type'] = $item['type'];
1896
		switch ($item['type']) {
1897
			case 'CNAME':
1898
				$newitem['data'] = $item['target'];
1899
				$resolved[] = $newitem;
1900
				break;
1901
			case 'A':
1902
				$newitem['data'] = $item['ip'];
1903
				$resolved[] = $newitem;
1904
				break;
1905
			case 'AAAA':
1906
				$newitem['data'] = $item['ipv6'];
1907
				$resolved[] = $newitem;
1908
				break;
1909
		}
1910
	}
1911
	if ($index == false) {
1912
		foreach ($resolved as $res) {
1913
			$noind[] = $res['data'];
1914
		}
1915
		$resolved = $noind;
1916
	}
1917
	return $resolved;
1918
}
1919

    
1920
function add_hostname_to_watch($hostname) {
1921
	if (!is_dir("/var/db/dnscache")) {
1922
		mkdir("/var/db/dnscache");
1923
	}
1924
	$result = array();
1925
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1926
		$contents = "";
1927
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1928
		if (!empty($domips)) {
1929
			foreach ($domips as $ip) {
1930
				$contents .= "$ip\n";
1931
			}
1932
		}
1933
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1934
		/* Remove empty elements */
1935
		$result = array_filter(explode("\n", $contents), 'strlen');
1936
	}
1937
	return $result;
1938
}
1939

    
1940
function is_fqdn($fqdn) {
1941
	return (bool) preg_match('/^(?:(?!-)[-_a-z0-9]+(?<!-)\.)*(?!-)[a-z0-9\-]+(?<!-)\.(?:xn--)?[a-z]+[\.]?$/i', $fqdn);
1942
}
1943

    
1944
function pfsense_default_state_size() {
1945
	/* get system memory amount */
1946
	$memory = get_memory();
1947
	$physmem = $memory[0];
1948
	/* Be cautious and only allocate 10% of system memory to the state table */
1949
	$max_states = (int) ($physmem/10)*1000;
1950
	return $max_states;
1951
}
1952

    
1953
function pfsense_default_tables_size() {
1954
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1955
	return $current;
1956
}
1957

    
1958
function pfsense_default_table_entries_size() {
1959
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1960
	return (trim($current));
1961
}
1962

    
1963
/* Compare the current hostname DNS to the DNS cache we made
1964
 * if it has changed we return the old records
1965
 * if no change we return false */
1966
function compare_hostname_to_dnscache($hostname) {
1967
	if (!is_dir("/var/db/dnscache")) {
1968
		mkdir("/var/db/dnscache");
1969
	}
1970
	$hostname = trim($hostname);
1971
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1972
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1973
	} else {
1974
		$oldcontents = "";
1975
	}
1976
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1977
		$contents = "";
1978
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1979
		if (!empty($domips)) {
1980
			foreach ($domips as $ip) {
1981
				$contents .= "$ip\n";
1982
			}
1983
		}
1984
	}
1985

    
1986
	if (trim($oldcontents) != trim($contents)) {
1987
		if ($g['debug']) {
1988
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1989
		}
1990
		return ($oldcontents);
1991
	} else {
1992
		return false;
1993
	}
1994
}
1995

    
1996
/*
1997
 * load_crypto() - Load crypto modules if enabled in config.
1998
 */
1999
function load_crypto() {
2000
	global $config, $g;
2001
	$crypto_modules = array('aesni', 'cryptodev');
2002

    
2003
	$enabled_modules = explode('_', $config['system']['crypto_hardware']);
2004

    
2005
	foreach ($enabled_modules as $enmod) {
2006
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
2007
			continue;
2008
		}
2009
		if (!is_module_loaded($enmod)) {
2010
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
2011
			mute_kernel_msgs();
2012
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
2013
			unmute_kernel_msgs();
2014
		}
2015
	}
2016
}
2017

    
2018
/*
2019
 * load_thermal_hardware() - Load temperature monitor kernel module
2020
 */
2021
function load_thermal_hardware() {
2022
	global $config, $g;
2023
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
2024

    
2025
	if (!in_array($config['system']['thermal_hardware'], $thermal_hardware_modules)) {
2026
		return false;
2027
	}
2028

    
2029
	if (!empty($config['system']['thermal_hardware']) && !is_module_loaded($config['system']['thermal_hardware'])) {
2030
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $config['system']['thermal_hardware']));
2031
		mwexec("/sbin/kldload {$config['system']['thermal_hardware']}");
2032
	}
2033
}
2034

    
2035
/****f* pfsense-utils/isvm
2036
 * NAME
2037
 *   isvm
2038
 * INPUTS
2039
 *	none
2040
 * RESULT
2041
 *   returns true if machine is running under a virtual environment
2042
 ******/
2043
function isvm() {
2044
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2045
	$_gb = exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2046

    
2047
	if ($rc != 0 || !isset($output[0])) {
2048
		return false;
2049
	}
2050

    
2051
	foreach ($virtualenvs as $virtualenv) {
2052
		if (stripos($output[0], $virtualenv) !== false) {
2053
			return true;
2054
		}
2055
	}
2056

    
2057
	return false;
2058
}
2059

    
2060
function get_freebsd_version() {
2061
	$version = explode(".", php_uname("r"));
2062
	return $version[0];
2063
}
2064

    
2065
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2066
	global $config, $g;
2067

    
2068
	$fp = fopen($destination, "wb");
2069

    
2070
	if (!$fp) {
2071
		return false;
2072
	}
2073

    
2074
	$ch = curl_init();
2075
	curl_setopt($ch, CURLOPT_URL, $url);
2076
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2077
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2078
	curl_setopt($ch, CURLOPT_FILE, $fp);
2079
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2080
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2081
	curl_setopt($ch, CURLOPT_HEADER, false);
2082
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2083
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2084
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2085
	} else {
2086
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2087
	}
2088

    
2089
	set_curlproxy($ch);
2090

    
2091
	@curl_exec($ch);
2092
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2093
	fclose($fp);
2094
	curl_close($ch);
2095
	if ($http_code == 200) {
2096
		return true;
2097
	} else {
2098
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2099
		unlink_if_exists($destination);
2100
		return false;
2101
	}
2102
}
2103

    
2104
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2105
	global $config, $g;
2106
	global $ch, $fout, $file_size, $downloaded, $config, $first_progress_update;
2107
	$file_size = 1;
2108
	$downloaded = 1;
2109
	$first_progress_update = TRUE;
2110
	/* open destination file */
2111
	$fout = fopen($destination, "wb");
2112

    
2113
	if (!$fout) {
2114
		return false;
2115
	}
2116
	/*
2117
	 *      Originally by Author: Keyvan Minoukadeh
2118
	 *      Modified by Scott Ullrich to return Content-Length size
2119
	 */
2120
	$ch = curl_init();
2121
	curl_setopt($ch, CURLOPT_URL, $url);
2122
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify_ssl);
2123
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify_ssl);
2124
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2125
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2126
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2127
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2128
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2129
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2130
	if (!isset($config['system']['do_not_send_uniqueid'])) {
2131
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version'] . ':' . system_get_uniqueid());
2132
	} else {
2133
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
2134
	}
2135

    
2136
	set_curlproxy($ch);
2137

    
2138
	@curl_exec($ch);
2139
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2140
	fclose($fout);
2141
	curl_close($ch);
2142
	if ($http_code == 200) {
2143
		return true;
2144
	} else {
2145
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2146
		unlink_if_exists($destination);
2147
		return false;
2148
	}
2149
}
2150

    
2151
function read_header($ch, $string) {
2152
	global $file_size, $fout;
2153
	$length = strlen($string);
2154
	$regs = "";
2155
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2156
	if ($regs[2] <> "") {
2157
		$file_size = intval($regs[2]);
2158
	}
2159
	ob_flush();
2160
	return $length;
2161
}
2162

    
2163
function read_body($ch, $string) {
2164
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2165
	global $pkg_interface;
2166
	$length = strlen($string);
2167
	$downloaded += intval($length);
2168
	if ($file_size > 0) {
2169
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2170
		$downloadProgress = 100 - $downloadProgress;
2171
	} else {
2172
		$downloadProgress = 0;
2173
	}
2174
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2175
		if ($sendto == "status") {
2176
			if ($pkg_interface == "console") {
2177
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2178
					$tostatus = $static_status . $downloadProgress . "%";
2179
					if ($downloadProgress == 100) {
2180
						$tostatus = $tostatus . "\r";
2181
					}
2182
					update_status($tostatus);
2183
				}
2184
			} else {
2185
				$tostatus = $static_status . $downloadProgress . "%";
2186
				update_status($tostatus);
2187
			}
2188
		} else {
2189
			if ($pkg_interface == "console") {
2190
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2191
					$tooutput = $static_output . $downloadProgress . "%";
2192
					if ($downloadProgress == 100) {
2193
						$tooutput = $tooutput . "\r";
2194
					}
2195
					update_output_window($tooutput);
2196
				}
2197
			} else {
2198
				$tooutput = $static_output . $downloadProgress . "%";
2199
				update_output_window($tooutput);
2200
			}
2201
		}
2202
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2203
			update_progress_bar($downloadProgress, $first_progress_update);
2204
			$first_progress_update = FALSE;
2205
		}
2206
		$lastseen = $downloadProgress;
2207
	}
2208
	if ($fout) {
2209
		fwrite($fout, $string);
2210
	}
2211
	ob_flush();
2212
	return $length;
2213
}
2214

    
2215
/*
2216
 *   update_output_window: update bottom textarea dynamically.
2217
 */
2218
function update_output_window($text) {
2219
	global $pkg_interface;
2220
	$log = preg_replace("/\n/", "\\n", $text);
2221
	if ($pkg_interface != "console") {
2222
?>
2223
<script type="text/javascript">
2224
//<![CDATA[
2225
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2226
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2227
//]]>
2228
</script>
2229
<?php
2230
	}
2231
	/* ensure that contents are written out */
2232
	ob_flush();
2233
}
2234

    
2235
/*
2236
 *   update_status: update top textarea dynamically.
2237
 */
2238
function update_status($status) {
2239
	global $pkg_interface;
2240

    
2241
	if ($pkg_interface == "console") {
2242
		print ("{$status}");
2243
	}
2244

    
2245
	/* ensure that contents are written out */
2246
	ob_flush();
2247
}
2248

    
2249
/*
2250
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2251
 */
2252
function update_progress_bar($percent, $first_time) {
2253
	global $pkg_interface;
2254
	if ($percent > 100) {
2255
		$percent = 1;
2256
	}
2257
	if ($pkg_interface <> "console") {
2258
		echo '<script type="text/javascript">';
2259
		echo "\n//<![CDATA[\n";
2260
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2261
		echo "\n//]]>\n";
2262
		echo '</script>';
2263
	} else {
2264
		if (!($first_time)) {
2265
			echo "\x08\x08\x08\x08\x08";
2266
		}
2267
		echo sprintf("%4d%%", $percent);
2268
	}
2269
}
2270

    
2271
function update_alias_name($new_alias_name, $orig_alias_name) {
2272
	if (!$orig_alias_name) {
2273
		return;
2274
	}
2275

    
2276
	// Firewall rules
2277
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2278
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2279
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2280
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2281
	// NAT Rules
2282
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2283
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2284
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2285
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2286
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2287
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2288
	// NAT 1:1 Rules
2289
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2290
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2291
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2292
	// NAT Outbound Rules
2293
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2294
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2295
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2296
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2297
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2298
	// Alias in an alias
2299
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2300
}
2301

    
2302
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2303
	global $g, $config, $pconfig, $debug;
2304
	if (!$origname) {
2305
		return;
2306
	}
2307

    
2308
	$sectionref = &$config;
2309
	foreach ($section as $sectionname) {
2310
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2311
			$sectionref = &$sectionref[$sectionname];
2312
		} else {
2313
			return;
2314
		}
2315
	}
2316

    
2317
	if ($debug) {
2318
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2319
		fwrite($fd, print_r($pconfig, true));
2320
	}
2321

    
2322
	if (is_array($sectionref)) {
2323
		foreach ($sectionref as $itemkey => $item) {
2324
			if ($debug) {
2325
				fwrite($fd, "$itemkey\n");
2326
			}
2327

    
2328
			$fieldfound = true;
2329
			$fieldref = &$sectionref[$itemkey];
2330
			foreach ($field as $fieldname) {
2331
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2332
					$fieldref = &$fieldref[$fieldname];
2333
				} else {
2334
					$fieldfound = false;
2335
					break;
2336
				}
2337
			}
2338
			if ($fieldfound && $fieldref == $origname) {
2339
				if ($debug) {
2340
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2341
				}
2342
				$fieldref = $new_alias_name;
2343
			}
2344
		}
2345
	}
2346

    
2347
	if ($debug) {
2348
		fclose($fd);
2349
	}
2350

    
2351
}
2352

    
2353
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2354
	/*
2355
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2356
	 * $type = if set to 'url' then subnets and ips will be returned,
2357
	 *         if set to 'url_ports' port-ranges and ports will be returned
2358
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2359
	 *
2360
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2361
	 */
2362

    
2363
	if (!file_exists($filename)) {
2364
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2365
		return null;
2366
	}
2367

    
2368
	if (filesize($filename) == 0) {
2369
		log_error(sprintf(gettext("Could not process empty file from alias: %s"), $filename));
2370
		return null;
2371
	}
2372
	$fd = @fopen($filename, 'r');
2373
	if (!$fd) {
2374
		log_error(sprintf(gettext("Could not process aliases from alias: %s"), $filename));
2375
		return null;
2376
	}
2377
	$items = array();
2378
	$comments = array();
2379
	/* NOTE: fgetss() is not a typo RTFM before being smart */
2380
	while (($fc = fgetss($fd)) !== FALSE) {
2381
		$tmp = alias_idn_to_ascii(trim($fc, " \t\n\r"));
2382
		if (empty($tmp)) {
2383
			continue;
2384
		}
2385
		if (($kflc) && (strpos($tmp, '#') === 0)) {	// Keep Full Line Comments (lines beginning with #).
2386
			$comments[] = $tmp;
2387
		} else {
2388
			$tmp_str = strstr($tmp, '#', true);
2389
			if (!empty($tmp_str)) {
2390
				$tmp = $tmp_str;
2391
			}
2392
			$tmp_str = strstr($tmp, ' ', true);
2393
			if (!empty($tmp_str)) {
2394
				$tmp = $tmp_str;
2395
			}
2396
			switch ($type) {
2397
				case "url":
2398
				case "urltable":
2399
					if (is_ipaddr($tmp) || is_subnet($tmp)) {
2400
						$items[] = $tmp;
2401
						break;
2402
					}
2403
					if (is_fqdn($tmp)) {
2404
						$results = resolve_host_addresses($tmp, array(DNS_A, DNS_AAAA), false);
2405
						if (!empty($results)) {
2406
							foreach ($results as $ip) {
2407
								if (is_ipaddr($ip)) {
2408
									$items[] = $ip;
2409
								}
2410
							}
2411
						}
2412
					}
2413
					break;
2414
				case "url_ports":
2415
				case "urltable_ports":
2416
					if (is_port_or_range($tmp)) {
2417
						$items[] = $tmp;
2418
					}
2419
					break;
2420
				default:
2421
					/* unknown type */
2422
					break;
2423
				}
2424
			if (count($items) == $max_items) {
2425
				break;
2426
			}
2427
		}
2428
	}
2429
	fclose($fd);
2430
	return array_merge($comments, $items);
2431
}
2432

    
2433
function update_alias_url_data() {
2434
	global $config, $g;
2435

    
2436
	$updated = false;
2437

    
2438
	/* item is a url type */
2439
	$lockkey = lock('aliasurl');
2440
	if (is_array($config['aliases']['alias'])) {
2441
		foreach ($config['aliases']['alias'] as $x => $alias) {
2442
			if (empty($alias['aliasurl'])) {
2443
				continue;
2444
			}
2445

    
2446
			$address = null;
2447
			foreach ($alias['aliasurl'] as $alias_url) {
2448
				/* fetch down and add in */
2449
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2450
				unlink($temp_filename);
2451
				$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2452
				mkdir($temp_filename);
2453
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2454
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2455
					continue;
2456
				}
2457

    
2458
				/* if the item is tar gzipped then extract */
2459
				if (stripos($alias_url, '.tgz')) {
2460
					if (!process_alias_tgz($temp_filename)) {
2461
						continue;
2462
					}
2463
				}
2464
				if (file_exists("{$temp_filename}/aliases")) {
2465
					$address = parse_aliases_file("{$temp_filename}/aliases", $alias['type'], 5000);
2466
					mwexec("/bin/rm -rf {$temp_filename}");
2467
				}
2468
			}
2469
			if ($address != null) {
2470
				$config['aliases']['alias'][$x]['address'] = implode(" ", $address);
2471
				$updated = true;
2472
			}
2473
		}
2474
	}
2475
	unlock($lockkey);
2476

    
2477
	/* Report status to callers as well */
2478
	return $updated;
2479
}
2480

    
2481
function process_alias_tgz($temp_filename) {
2482
	if (!file_exists('/usr/bin/tar')) {
2483
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2484
		return false;
2485
	}
2486
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2487
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2488
	unlink("{$temp_filename}/aliases.tgz");
2489
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2490
	/* foreach through all extracted files and build up aliases file */
2491
	$fd = @fopen("{$temp_filename}/aliases", "w");
2492
	if (!$fd) {
2493
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2494
		return false;
2495
	}
2496
	foreach ($files_to_process as $f2p) {
2497
		$tmpfd = @fopen($f2p, 'r');
2498
		if (!$tmpfd) {
2499
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2500
			continue;
2501
		}
2502
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2503
			fwrite($fd, $tmpbuf);
2504
		}
2505
		fclose($tmpfd);
2506
		unlink($f2p);
2507
	}
2508
	fclose($fd);
2509
	unset($tmpbuf);
2510

    
2511
	return true;
2512
}
2513

    
2514
function version_compare_dates($a, $b) {
2515
	$a_time = strtotime($a);
2516
	$b_time = strtotime($b);
2517

    
2518
	if ((!$a_time) || (!$b_time)) {
2519
		return FALSE;
2520
	} else {
2521
		if ($a_time < $b_time) {
2522
			return -1;
2523
		} elseif ($a_time == $b_time) {
2524
			return 0;
2525
		} else {
2526
			return 1;
2527
		}
2528
	}
2529
}
2530
function version_get_string_value($a) {
2531
	$strs = array(
2532
		0 => "ALPHA-ALPHA",
2533
		2 => "ALPHA",
2534
		3 => "BETA",
2535
		4 => "B",
2536
		5 => "C",
2537
		6 => "D",
2538
		7 => "RC",
2539
		8 => "RELEASE",
2540
		9 => "*"			// Matches all release levels
2541
	);
2542
	$major = 0;
2543
	$minor = 0;
2544
	foreach ($strs as $num => $str) {
2545
		if (substr($a, 0, strlen($str)) == $str) {
2546
			$major = $num;
2547
			$n = substr($a, strlen($str));
2548
			if (is_numeric($n)) {
2549
				$minor = $n;
2550
			}
2551
			break;
2552
		}
2553
	}
2554
	return "{$major}.{$minor}";
2555
}
2556
function version_compare_string($a, $b) {
2557
	// Only compare string parts if both versions give a specific release
2558
	// (If either version lacks a string part, assume intended to match all release levels)
2559
	if (isset($a) && isset($b)) {
2560
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2561
	} else {
2562
		return 0;
2563
	}
2564
}
2565
function version_compare_numeric($a, $b) {
2566
	$a_arr = explode('.', rtrim($a, '.'));
2567
	$b_arr = explode('.', rtrim($b, '.'));
2568

    
2569
	foreach ($a_arr as $n => $val) {
2570
		if (array_key_exists($n, $b_arr)) {
2571
			// So far so good, both have values at this minor version level. Compare.
2572
			if ($val > $b_arr[$n]) {
2573
				return 1;
2574
			} elseif ($val < $b_arr[$n]) {
2575
				return -1;
2576
			}
2577
		} else {
2578
			// a is greater, since b doesn't have any minor version here.
2579
			return 1;
2580
		}
2581
	}
2582
	if (count($b_arr) > count($a_arr)) {
2583
		// b is longer than a, so it must be greater.
2584
		return -1;
2585
	} else {
2586
		// Both a and b are of equal length and value.
2587
		return 0;
2588
	}
2589
}
2590
function pfs_version_compare($cur_time, $cur_text, $remote) {
2591
	// First try date compare
2592
	$v = version_compare_dates($cur_time, $remote);
2593
	if ($v === FALSE) {
2594
		// If that fails, try to compare by string
2595
		// Before anything else, simply test if the strings are equal
2596
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2597
			return 0;
2598
		}
2599
		list($cur_num, $cur_str) = explode('-', $cur_text);
2600
		list($rem_num, $rem_str) = explode('-', $remote);
2601

    
2602
		// First try to compare the numeric parts of the version string.
2603
		$v = version_compare_numeric($cur_num, $rem_num);
2604

    
2605
		// If the numeric parts are the same, compare the string parts.
2606
		if ($v == 0) {
2607
			return version_compare_string($cur_str, $rem_str);
2608
		}
2609
	}
2610
	return $v;
2611
}
2612
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2613
	global $g, $config;
2614

    
2615
	$urltable_prefix = "/var/db/aliastables/";
2616
	$urltable_filename = $urltable_prefix . $name . ".txt";
2617
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2618

    
2619
	// Make the aliases directory if it doesn't exist
2620
	if (!file_exists($urltable_prefix)) {
2621
		mkdir($urltable_prefix);
2622
	} elseif (!is_dir($urltable_prefix)) {
2623
		unlink($urltable_prefix);
2624
		mkdir($urltable_prefix);
2625
	}
2626

    
2627
	// If the file doesn't exist or is older than update_freq days, fetch a new copy.
2628
	if (!file_exists($urltable_filename) || (filesize($urltable_filename) == "0") ||
2629
	    ((time() - filemtime($urltable_filename)) > ($freq * 86400 - 90)) ||
2630
	    $forceupdate) {
2631

    
2632
		// Try to fetch the URL supplied
2633
		unlink_if_exists($tmp_urltable_filename);
2634
		$verify_ssl = isset($config['system']['checkaliasesurlcert']);
2635
		if (download_file($url, $tmp_urltable_filename, $verify_ssl)) {
2636
			// Convert lines that begin with '$' or ';' to comments '#' instead of deleting them.
2637
			mwexec("/usr/bin/sed -i \"\" -E 's/^[[:space:]]*($|#|;)/#/g; /^#/!s/\;.*//g;' ". escapeshellarg($tmp_urltable_filename));
2638

    
2639
			$type = ($type) ? $type : alias_get_type($name);	// If empty type passed, try to get it from config.
2640

    
2641
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2642
			if ($type == "urltable_ports") {
2643
				$parsed_contents = group_ports($parsed_contents, true);
2644
			}
2645
			if (is_array($parsed_contents)) {
2646
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2647
			} else {
2648
				touch($urltable_filename);
2649
			}
2650

    
2651
			/* Remove existing archive and create an up to date archive if RAM disk is enabled. */
2652
			unlink_if_exists("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz");
2653
			if (isset($config['system']['use_mfs_tmpvar']) && !file_exists("/conf/ram_disks_failed")) {
2654
				mwexec("/usr/bin/tar -czf " . escapeshellarg("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz") . " -C / " . escapeshellarg($urltable_filename));
2655
			}
2656

    
2657
			unlink_if_exists($tmp_urltable_filename);
2658
		} else {
2659
			if (!$validateonly) {
2660
				touch($urltable_filename);
2661
			}
2662
			return false;
2663
		}
2664
		return true;
2665
	} else {
2666
		// File exists, and it doesn't need to be updated.
2667
		return -1;
2668
	}
2669
}
2670

    
2671
function get_include_contents($filename) {
2672
	if (is_file($filename)) {
2673
		ob_start();
2674
		include $filename;
2675
		$contents = ob_get_contents();
2676
		ob_end_clean();
2677
		return $contents;
2678
	}
2679
	return false;
2680
}
2681

    
2682
/* This xml 2 array function is courtesy of the php.net comment section on xml_parse.
2683
 * it is roughly 4 times faster then our existing pfSense parser but due to the large
2684
 * size of the RRD xml dumps this is required.
2685
 * The reason we do not use it for pfSense is that it does not know about array fields
2686
 * which causes it to fail on array fields with single items. Possible Todo?
2687
 */
2688
function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
2689
	if (!function_exists('xml_parser_create')) {
2690
		return array ();
2691
	}
2692
	$parser = xml_parser_create('');
2693
	xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
2694
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2695
	xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2696
	xml_parse_into_struct($parser, trim($contents), $xml_values);
2697
	xml_parser_free($parser);
2698
	if (!$xml_values) {
2699
		return; //Hmm...
2700
	}
2701
	$xml_array = array ();
2702
	$parents = array ();
2703
	$opened_tags = array ();
2704
	$arr = array ();
2705
	$current = & $xml_array;
2706
	$repeated_tag_index = array ();
2707
	foreach ($xml_values as $data) {
2708
		unset ($attributes, $value);
2709
		extract($data);
2710
		$result = array ();
2711
		$attributes_data = array ();
2712
		if (isset ($value)) {
2713
			if ($priority == 'tag') {
2714
				$result = $value;
2715
			} else {
2716
				$result['value'] = $value;
2717
			}
2718
		}
2719
		if (isset ($attributes) and $get_attributes) {
2720
			foreach ($attributes as $attr => $val) {
2721
				if ($priority == 'tag') {
2722
					$attributes_data[$attr] = $val;
2723
				} else {
2724
					$result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
2725
				}
2726
			}
2727
		}
2728
		if ($type == "open") {
2729
			$parent[$level -1] = & $current;
2730
			if (!is_array($current) or (!in_array($tag, array_keys($current)))) {
2731
				$current[$tag] = $result;
2732
				if ($attributes_data) {
2733
					$current[$tag . '_attr'] = $attributes_data;
2734
				}
2735
				$repeated_tag_index[$tag . '_' . $level] = 1;
2736
				$current = &$current[$tag];
2737
			} else {
2738
				if (isset ($current[$tag][0])) {
2739
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2740
					$repeated_tag_index[$tag . '_' . $level]++;
2741
				} else {
2742
					$current[$tag] = array (
2743
						$current[$tag],
2744
						$result
2745
						);
2746
					$repeated_tag_index[$tag . '_' . $level] = 2;
2747
					if (isset ($current[$tag . '_attr'])) {
2748
						$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2749
						unset ($current[$tag . '_attr']);
2750
					}
2751
				}
2752
				$last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
2753
				$current = &$current[$tag][$last_item_index];
2754
			}
2755
		} elseif ($type == "complete") {
2756
			if (!isset ($current[$tag])) {
2757
				$current[$tag] = $result;
2758
				$repeated_tag_index[$tag . '_' . $level] = 1;
2759
				if ($priority == 'tag' and $attributes_data) {
2760
					$current[$tag . '_attr'] = $attributes_data;
2761
				}
2762
			} else {
2763
				if (isset ($current[$tag][0]) and is_array($current[$tag])) {
2764
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2765
					if ($priority == 'tag' and $get_attributes and $attributes_data) {
2766
						$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2767
					}
2768
					$repeated_tag_index[$tag . '_' . $level]++;
2769
				} else {
2770
					$current[$tag] = array (
2771
						$current[$tag],
2772
						$result
2773
						);
2774
					$repeated_tag_index[$tag . '_' . $level] = 1;
2775
					if ($priority == 'tag' and $get_attributes) {
2776
						if (isset ($current[$tag . '_attr'])) {
2777
							$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2778
							unset ($current[$tag . '_attr']);
2779
						}
2780
						if ($attributes_data) {
2781
							$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2782
						}
2783
					}
2784
					$repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
2785
				}
2786
			}
2787
		} elseif ($type == 'close') {
2788
			$current = &$parent[$level -1];
2789
		}
2790
	}
2791
	return ($xml_array);
2792
}
2793

    
2794
function get_country_name($country_code = "ALL") {
2795
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2796
		return "";
2797
	}
2798

    
2799
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2800
	$country_names_contents = file_get_contents($country_names_xml);
2801
	$country_names = xml2array($country_names_contents);
2802

    
2803
	if ($country_code == "ALL") {
2804
		$country_list = array();
2805
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2806
			$country_list[] = array(
2807
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2808
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2809
		}
2810
		return $country_list;
2811
	}
2812

    
2813
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2814
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2815
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2816
		}
2817
	}
2818
	return "";
2819
}
2820

    
2821
/* Return the list of country codes to be used on CAs and certs */
2822
function get_cert_country_codes() {
2823
	$countries = get_country_name();
2824

    
2825
	$country_codes = array();
2826
	foreach ($countries as $country) {
2827
		$country_codes[$country['code']] = $country['code'];
2828
	}
2829
	ksort($country_codes);
2830

    
2831
	/* Preserve historical order: None, US, CA, other countries */
2832
	$first_items[''] = gettext("None");
2833
	$first_items['US'] = $country_codes['US'];
2834
	$first_items['CA'] = $country_codes['CA'];
2835
	unset($country_codes['US']);
2836
	unset($country_codes['CA']);
2837

    
2838
	return array_merge($first_items, $country_codes);
2839
}
2840

    
2841
/* sort by interface only, retain the original order of rules that apply to
2842
   the same interface */
2843
function filter_rules_sort() {
2844
	global $config;
2845

    
2846
	init_config_arr(array('filter', 'rule'));
2847
	/* mark each rule with the sequence number (to retain the order while sorting) */
2848
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2849
		if (!is_array($config['filter']['rule'][$i])) {
2850
			$config['filter']['rule'][$i] = array();
2851
		}
2852
		$config['filter']['rule'][$i]['seq'] = $i;
2853
	}
2854

    
2855
	usort($config['filter']['rule'], "filter_rules_compare");
2856

    
2857
	/* strip the sequence numbers again */
2858
	for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
2859
		unset($config['filter']['rule'][$i]['seq']);
2860
	}
2861
}
2862
function filter_rules_compare($a, $b) {
2863
	if (isset($a['floating']) && isset($b['floating'])) {
2864
		return $a['seq'] - $b['seq'];
2865
	} else if (isset($a['floating'])) {
2866
		return -1;
2867
	} else if (isset($b['floating'])) {
2868
		return 1;
2869
	} else if ($a['interface'] == $b['interface']) {
2870
		return $a['seq'] - $b['seq'];
2871
	} else {
2872
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2873
	}
2874
}
2875

    
2876
function generate_ipv6_from_mac($mac) {
2877
	$elements = explode(":", $mac);
2878
	if (count($elements) <> 6) {
2879
		return false;
2880
	}
2881

    
2882
	$i = 0;
2883
	$ipv6 = "fe80::";
2884
	foreach ($elements as $byte) {
2885
		if ($i == 0) {
2886
			$hexadecimal = substr($byte, 1, 2);
2887
			$bitmap = base_convert($hexadecimal, 16, 2);
2888
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2889
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2890
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2891
		}
2892
		$ipv6 .= $byte;
2893
		if ($i == 1) {
2894
			$ipv6 .= ":";
2895
		}
2896
		if ($i == 3) {
2897
			$ipv6 .= ":";
2898
		}
2899
		if ($i == 2) {
2900
			$ipv6 .= "ff:fe";
2901
		}
2902

    
2903
		$i++;
2904
	}
2905
	return $ipv6;
2906
}
2907

    
2908
/****f* pfsense-utils/load_mac_manufacturer_table
2909
 * NAME
2910
 *   load_mac_manufacturer_table
2911
 * INPUTS
2912
 *   none
2913
 * RESULT
2914
 *   returns associative array with MAC-Manufacturer pairs
2915
 ******/
2916
function load_mac_manufacturer_table() {
2917
	/* load MAC-Manufacture data from the file */
2918
	$macs = false;
2919
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2920
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2921
	}
2922
	if ($macs) {
2923
		foreach ($macs as $line) {
2924
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2925
				/* store values like this $mac_man['000C29']='VMware' */
2926
				$mac_man["$matches[1]"] = $matches[2];
2927
			}
2928
		}
2929
		return $mac_man;
2930
	} else {
2931
		return -1;
2932
	}
2933

    
2934
}
2935

    
2936
/****f* pfsense-utils/is_ipaddr_configured
2937
 * NAME
2938
 *   is_ipaddr_configured
2939
 * INPUTS
2940
 *   IP Address to check.
2941
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2942
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2943
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2944
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2945
 *     If check_subnets is true and cidrprefix is specified,
2946
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2947
 * RESULT
2948
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2949
*/
2950
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2951
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2952
		return true;
2953
	}
2954
	return false;
2955
}
2956

    
2957
/****f* pfsense-utils/where_is_ipaddr_configured
2958
 * NAME
2959
 *   where_is_ipaddr_configured
2960
 * INPUTS
2961
 *   IP Address to check.
2962
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2963
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2964
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2965
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2966
 *     If check_subnets is true and cidrprefix is specified,
2967
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2968
 * RESULT
2969
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
2970
 *   If there are no matches then an empty array is returned.
2971
*/
2972
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2973
	global $config;
2974

    
2975
	$where_configured = array();
2976

    
2977
	$pos = strpos($ignore_if, '_virtualip');
2978
	if ($pos !== false) {
2979
		$ignore_vip_id = substr($ignore_if, $pos+10);
2980
		$ignore_vip_if = substr($ignore_if, 0, $pos);
2981
	} else {
2982
		$ignore_vip_id = -1;
2983
		$ignore_vip_if = $ignore_if;
2984
	}
2985

    
2986
	$isipv6 = is_ipaddrv6($ipaddr);
2987

    
2988
	if ($isipv6) {
2989
		$ipaddr = text_to_compressed_ip6($ipaddr);
2990
	}
2991

    
2992
	if ($check_subnets) {
2993
		$cidrprefix = intval($cidrprefix);
2994
		if ($isipv6) {
2995
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
2996
				$cidrprefix = 128;
2997
			}
2998
		} else {
2999
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3000
				$cidrprefix = 32;
3001
			}
3002
		}
3003
		$iflist = get_configured_interface_list();
3004
		foreach ($iflist as $if => $ifname) {
3005
			if ($ignore_if == $if) {
3006
				continue;
3007
			}
3008

    
3009
			if ($isipv6) {
3010
				$if_ipv6 = get_interface_ipv6($if);
3011
				$if_snbitsv6 = get_interface_subnetv6($if);
3012
				if ($if_ipv6 && $if_snbitsv6 && check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3013
					$where_entry = array();
3014
					$where_entry['if'] = $if;
3015
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3016
					$where_configured[] = $where_entry;
3017
				}
3018
			} else {
3019
				$if_ipv4 = get_interface_ip($if);
3020
				$if_snbitsv4 = get_interface_subnet($if);
3021
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3022
					$where_entry = array();
3023
					$where_entry['if'] = $if;
3024
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3025
					$where_configured[] = $where_entry;
3026
				}
3027
			}
3028
		}
3029
	} else {
3030
		if ($isipv6) {
3031
			$interface_list_ips = get_configured_ipv6_addresses();
3032
		} else {
3033
			$interface_list_ips = get_configured_ip_addresses();
3034
		}
3035

    
3036
		foreach ($interface_list_ips as $if => $ilips) {
3037
			if ($ignore_if == $if) {
3038
				continue;
3039
			}
3040
			if (strcasecmp($ipaddr, $ilips) == 0) {
3041
				$where_entry = array();
3042
				$where_entry['if'] = $if;
3043
				$where_entry['ip_or_subnet'] = $ilips;
3044
				$where_configured[] = $where_entry;
3045
			}
3046
		}
3047
	}
3048

    
3049
	if ($check_localip) {
3050
		if (!is_array($config['l2tp']) && !empty($config['l2tp']['localip']) && (strcasecmp($ipaddr, text_to_compressed_ip6($config['l2tp']['localip'])) == 0)) {
3051
			$where_entry = array();
3052
			$where_entry['if'] = 'l2tp';
3053
			$where_entry['ip_or_subnet'] = $config['l2tp']['localip'];
3054
			$where_configured[] = $where_entry;
3055
		}
3056
	}
3057

    
3058
	return $where_configured;
3059
}
3060

    
3061
/****f* pfsense-utils/pfSense_handle_custom_code
3062
 * NAME
3063
 *   pfSense_handle_custom_code
3064
 * INPUTS
3065
 *   directory name to process
3066
 * RESULT
3067
 *   globs the directory and includes the files
3068
 */
3069
function pfSense_handle_custom_code($src_dir) {
3070
	// Allow extending of the nat edit page and include custom input validation
3071
	if (is_dir("$src_dir")) {
3072
		$cf = glob($src_dir . "/*.inc");
3073
		foreach ($cf as $nf) {
3074
			if ($nf == "." || $nf == "..") {
3075
				continue;
3076
			}
3077
			// Include the extra handler
3078
			include_once("$nf");
3079
		}
3080
	}
3081
}
3082

    
3083
function set_language() {
3084
	global $config, $g;
3085

    
3086
	if (!empty($config['system']['language'])) {
3087
		$lang = $config['system']['language'];
3088
	} elseif (!empty($g['language'])) {
3089
		$lang = $g['language'];
3090
	}
3091
	$lang .= ".UTF-8";
3092

    
3093
	putenv("LANG={$lang}");
3094
	setlocale(LC_ALL, $lang);
3095
	textdomain("pfSense");
3096
	bindtextdomain("pfSense", "/usr/local/share/locale");
3097
	bind_textdomain_codeset("pfSense", $lang);
3098
}
3099

    
3100
function get_locale_list() {
3101
	$locales = array(
3102
		"bs" => gettext("Bosnian"),
3103
		"zh_CN" => gettext("Chinese"),
3104
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3105
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3106
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3107
		"nl" => gettext("Dutch"),
3108
		"en_US" => gettext("English"),
3109
		"fr" => gettext("French"),
3110
		"de_DE" => gettext("German (Germany)"),
3111
		"it" => gettext("Italian"),
3112
		"ko" => gettext("Korean"),
3113
		"nb" => gettext("Norwegian Bokmål"),
3114
		"pl" => gettext("Polish"),
3115
		"pt_PT" => gettext("Portuguese"),
3116
		"pt_BR" => gettext("Portuguese (Brazil)"),
3117
		"ru" => gettext("Russian"),
3118
		"es" => gettext("Spanish"),
3119
		"es_AR" => gettext("Spanish (Argentina)"),
3120
	);
3121

    
3122
	// If the locales are sorted, the order changes depending on the language selected. If the user accidentally
3123
	// selects the wrong language, this makes it very difficult to guess the intended language. NOT sorting
3124
	// allows the user to remember that English (say) is the seventh on the list and to get back to it more easily
3125

    
3126
	//asort($locales);
3127

    
3128
	return $locales;
3129
}
3130

    
3131
function return_hex_ipv4($ipv4) {
3132
	if (!is_ipaddrv4($ipv4)) {
3133
		return(false);
3134
	}
3135

    
3136
	/* we need the hex form of the interface IPv4 address */
3137
	$ip4arr = explode(".", $ipv4);
3138
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3139
}
3140

    
3141
function convert_ipv6_to_128bit($ipv6) {
3142
	if (!is_ipaddrv6($ipv6)) {
3143
		return(false);
3144
	}
3145

    
3146
	$ip6arr = array();
3147
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3148
	$ip6arr = explode(":", $ip6prefix);
3149
	/* binary presentation of the prefix for all 128 bits. */
3150
	$ip6prefixbin = "";
3151
	foreach ($ip6arr as $element) {
3152
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3153
	}
3154
	return($ip6prefixbin);
3155
}
3156

    
3157
function convert_128bit_to_ipv6($ip6bin) {
3158
	if (strlen($ip6bin) <> 128) {
3159
		return(false);
3160
	}
3161

    
3162
	$ip6arr = array();
3163
	$ip6binarr = array();
3164
	$ip6binarr = str_split($ip6bin, 16);
3165
	foreach ($ip6binarr as $binpart) {
3166
		$ip6arr[] = dechex(bindec($binpart));
3167
	}
3168
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3169

    
3170
	return($ip6addr);
3171
}
3172

    
3173

    
3174
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3175
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3176
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3177
/* 6to4 is 16 bits, e.g. 65535 */
3178
function calculate_ipv6_delegation_length($if) {
3179
	global $config;
3180

    
3181
	if (!is_array($config['interfaces'][$if])) {
3182
		return false;
3183
	}
3184

    
3185
	switch ($config['interfaces'][$if]['ipaddrv6']) {
3186
		case "6to4":
3187
			$pdlen = 16;
3188
			break;
3189
		case "6rd":
3190
			$rd6cfg = $config['interfaces'][$if];
3191
			$rd6plen = explode("/", $rd6cfg['prefix-6rd']);
3192
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $rd6cfg['prefix-6rd-v4plen'])));
3193
			break;
3194
		case "dhcp6":
3195
			$dhcp6cfg = $config['interfaces'][$if];
3196
			$pdlen = $dhcp6cfg['dhcp6-ia-pd-len'];
3197
			break;
3198
		default:
3199
			$pdlen = 0;
3200
			break;
3201
	}
3202
	return($pdlen);
3203
}
3204

    
3205
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3206
	$prefix = Net_IPv6::uncompress($prefix, true);
3207
	$suffix = Net_IPv6::uncompress($suffix, true);
3208

    
3209
	/*
3210
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3211
	 *                ^^^^ ^
3212
	 *                |||| \-> 64
3213
	 *                |||\---> 63, 62, 61, 60
3214
	 *                ||\----> 56
3215
	 *                |\-----> 52
3216
	 *                \------> 48
3217
	 */
3218

    
3219
	switch ($len) {
3220
	case 48:
3221
		$prefix_len = 15;
3222
		break;
3223
	case 52:
3224
		$prefix_len = 16;
3225
		break;
3226
	case 56:
3227
		$prefix_len = 17;
3228
		break;
3229
	case 59:
3230
	case 60:
3231
		$prefix_len = 18;
3232
		break;
3233
	/*
3234
	 * XXX 63, 62 and 61 should use 18 but PD can change and if
3235
	 * we let user chose this bit it can end up out of PD network
3236
	 *
3237
	 * Leave this with 20 for now until we find a way to let user
3238
	 * chose it. The side-effect is users with PD with one of these
3239
	 * lengths will not be able to setup DHCP server range for full
3240
	 * PD size, only for last /64 network
3241
	 */
3242
	case 63:
3243
	case 62:
3244
	case 61:
3245
	default:
3246
		$prefix_len = 20;
3247
		break;
3248
	}
3249

    
3250
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3251
	    substr($suffix, $prefix_len));
3252
}
3253

    
3254
function dhcpv6_pd_str_help($pdlen) {
3255
	$result = '';
3256

    
3257
	switch ($pdlen) {
3258
	case 48:
3259
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3260
		break;
3261
	case 52:
3262
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3263
		break;
3264
	case 56:
3265
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3266
		break;
3267
	case 59:
3268
	case 60:
3269
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3270
		break;
3271
	/*
3272
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3273
	 * we let the user choose this bit it can end up out of PD network
3274
	 *
3275
	 * Leave this with the same as 64 for now until we find a way to
3276
	 * let the user choose it. The side-effect is users with PD with one
3277
	 * of these lengths will not be able to setup DHCP server ranges
3278
	 * for full PD size, only for last /64 network
3279
	 */
3280
	case 61:
3281
	case 62:
3282
	case 63:
3283
	case 64:
3284
	default:
3285
		$result = '::xxxx:xxxx:xxxx:xxxx';
3286
		break;
3287
	}
3288

    
3289
	return $result;
3290
}
3291

    
3292
function huawei_rssi_to_string($rssi) {
3293
	$dbm = array();
3294
	$i = 0;
3295
	$dbstart = -113;
3296
	while ($i < 32) {
3297
		$dbm[$i] = $dbstart + ($i * 2);
3298
		$i++;
3299
	}
3300
	$percent = round(($rssi / 31) * 100);
3301
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3302
	return $string;
3303
}
3304

    
3305
function huawei_mode_to_string($mode, $submode) {
3306
	$modes[0] = gettext("None");
3307
	$modes[1] = "AMPS";
3308
	$modes[2] = "CDMA";
3309
	$modes[3] = "GSM/GPRS";
3310
	$modes[4] = "HDR";
3311
	$modes[5] = "WCDMA";
3312
	$modes[6] = "GPS";
3313

    
3314
	$submodes[0] = gettext("No Service");
3315
	$submodes[1] = "GSM";
3316
	$submodes[2] = "GPRS";
3317
	$submodes[3] = "EDGE";
3318
	$submodes[4] = "WCDMA";
3319
	$submodes[5] = "HSDPA";
3320
	$submodes[6] = "HSUPA";
3321
	$submodes[7] = "HSDPA+HSUPA";
3322
	$submodes[8] = "TD-SCDMA";
3323
	$submodes[9] = "HSPA+";
3324
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3325
	return $string;
3326
}
3327

    
3328
function huawei_service_to_string($state) {
3329
	$modes[0] = gettext("No Service");
3330
	$modes[1] = gettext("Restricted Service");
3331
	$modes[2] = gettext("Valid Service");
3332
	$modes[3] = gettext("Restricted Regional Service");
3333
	$modes[4] = gettext("Powersaving Service");
3334
	$modes[255] = gettext("Unknown Service");
3335
	$string = $modes[$state];
3336
	return $string;
3337
}
3338

    
3339
function huawei_simstate_to_string($state) {
3340
	$modes[0] = gettext("Invalid SIM/locked State");
3341
	$modes[1] = gettext("Valid SIM State");
3342
	$modes[2] = gettext("Invalid SIM CS State");
3343
	$modes[3] = gettext("Invalid SIM PS State");
3344
	$modes[4] = gettext("Invalid SIM CS/PS State");
3345
	$modes[255] = gettext("Missing SIM State");
3346
	$string = $modes[$state];
3347
	return $string;
3348
}
3349

    
3350
function zte_rssi_to_string($rssi) {
3351
	return huawei_rssi_to_string($rssi);
3352
}
3353

    
3354
function zte_mode_to_string($mode, $submode) {
3355
	$modes[0] = gettext("No Service");
3356
	$modes[1] = gettext("Limited Service");
3357
	$modes[2] = "GPRS";
3358
	$modes[3] = "GSM";
3359
	$modes[4] = "UMTS";
3360
	$modes[5] = "EDGE";
3361
	$modes[6] = "HSDPA";
3362

    
3363
	$submodes[0] = "CS_ONLY";
3364
	$submodes[1] = "PS_ONLY";
3365
	$submodes[2] = "CS_PS";
3366
	$submodes[3] = "CAMPED";
3367
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3368
	return $string;
3369
}
3370

    
3371
function zte_service_to_string($service) {
3372
	$modes[0] = gettext("Initializing Service");
3373
	$modes[1] = gettext("Network Lock error Service");
3374
	$modes[2] = gettext("Network Locked Service");
3375
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3376
	$string = $modes[$service];
3377
	return $string;
3378
}
3379

    
3380
function zte_simstate_to_string($state) {
3381
	$modes[0] = gettext("No action State");
3382
	$modes[1] = gettext("Network lock State");
3383
	$modes[2] = gettext("(U)SIM card lock State");
3384
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3385
	$string = $modes[$state];
3386
	return $string;
3387
}
3388

    
3389
function get_configured_pppoe_server_interfaces() {
3390
	global $config;
3391
	$iflist = array();
3392
	if (is_array($config['pppoes']['pppoe'])) {
3393
		foreach ($config['pppoes']['pppoe'] as $pppoe) {
3394
			if ($pppoe['mode'] == "server") {
3395
				$int = "poes". $pppoe['pppoeid'];
3396
				$iflist[$int] = strtoupper($int);
3397
			}
3398
		}
3399
	}
3400
	return $iflist;
3401
}
3402

    
3403
function get_pppoes_child_interfaces($ifpattern) {
3404
	$if_arr = array();
3405
	if ($ifpattern == "") {
3406
		return;
3407
	}
3408

    
3409
	exec("/sbin/ifconfig", $out, $ret);
3410
	foreach ($out as $line) {
3411
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3412
			$if_arr[] = $match[1];
3413
		}
3414
	}
3415
	return $if_arr;
3416

    
3417
}
3418

    
3419
/****f* pfsense-utils/pkg_call_plugins
3420
 * NAME
3421
 *   pkg_call_plugins
3422
 * INPUTS
3423
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3424
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3425
 * RESULT
3426
 *   returns associative array results from the plugin calls for each package
3427
 * NOTES
3428
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3429
 ******/
3430
function pkg_call_plugins($plugin_type, $plugin_params) {
3431
	global $g, $config;
3432
	$results = array();
3433
	if (!is_array($config['installedpackages']['package'])) {
3434
		return $results;
3435
	}
3436
	foreach ($config['installedpackages']['package'] as $package) {
3437
		if (is_array($package['plugins']['item'])) {
3438
			foreach ($package['plugins']['item'] as $plugin) {
3439
				if ($plugin['type'] == $plugin_type) {
3440
					if (file_exists($package['include_file'])) {
3441
						require_once($package['include_file']);
3442
					} else {
3443
						continue;
3444
					}
3445
					$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3446
					$plugin_function = $pkgname . '_'. $plugin_type;
3447
					$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3448
				}
3449
			}
3450
		}
3451
	}
3452
	return $results;
3453
}
3454

    
3455
// Convert IPv6 addresses to lower case
3456
function addrtolower($ip) {
3457
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3458
		return(strtolower($ip));
3459
	} else {
3460
		return($ip);
3461
	}
3462
}
3463

    
3464
function compare_by_name($a, $b) {
3465
	return strcasecmp($a['name'], $b['name']);
3466
}
3467

    
3468
/****f* pfsense-utils/getarraybyref
3469
 * NAME
3470
 *   getarraybyref
3471
 * INPUTS
3472
 *   $array the array of which a items array needs to be found.
3473
 *   $args.. the sub-items to be retrieved/created
3474
 * RESULT
3475
 *   returns the array that was retrieved from configuration, its created if it does not exist
3476
 * NOTES
3477
 *   Used by haproxy / acme / others.?. .
3478
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3479
 ******/
3480
function &getarraybyref(&$array) {
3481
	if (!isset($array)) {
3482
		return false;
3483
	}
3484
	if (!is_array($array)) {
3485
		$array = array();
3486
	}
3487
	$item = &$array;
3488
	$arg = func_get_args();
3489
	for($i = 1; $i < count($arg); $i++) {
3490
		$itemindex = $arg[$i];
3491
		if (!is_array($item[$itemindex])) {
3492
			$item[$itemindex] = array();
3493
		}
3494
		$item = &$item[$itemindex];
3495
	}
3496
	return $item;
3497
}
3498

    
3499
/****f* pfsense-utils/send_download_data
3500
 * NAME
3501
 *   send_download_data - Send content to a user's browser as a file to download
3502
 * INPUTS
3503
 *   $type        : The type of download, either 'data' to send the contents of
3504
 *                    a variable or 'file' to send the contents of a file on the
3505
 *                    filesystem.
3506
 *   $content     : For 'data' type, the content to send the user. For 'file'
3507
 *                    type, the full path to the file to send.
3508
 *   $userfilename: The filename presented to the user when downloading. For
3509
 *                    'file' type, this may be omitted and the basename of
3510
 *                    $content will be used instead.
3511
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3512
 * RESULT
3513
 *   Sends the data to the browser as a file to download.
3514
 ******/
3515

    
3516
function send_user_download($type = 'data', $content, $userfilename = "", $contenttype = "application/octet-stream") {
3517
	/* If the type is 'file', then use the file size, otherwise use the size of the data to send */
3518
	$size = ($type == 'file') ? filesize($content) : strlen($content);
3519

    
3520
	/* If the filename to pass to the user is empty, assume it to be the filename being read. */
3521
	$name = basename((($type == 'file') && empty($userfilename)) ? $content : $userfilename);
3522

    
3523
	/* Cannot determine the filename, so bail. */
3524
	if (empty($name)) {
3525
		exit;
3526
	}
3527

    
3528
	/* Send basic download headers */
3529
	header("Content-Type: {$contenttype}");
3530
	header("Content-Length: {$size}");
3531
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3532

    
3533
	/* Send cache headers */
3534
	if (isset($_SERVER['HTTPS'])) {
3535
		header('Pragma: ');
3536
		header('Cache-Control: ');
3537
	} else {
3538
		header("Pragma: private");
3539
		header("Cache-Control: private, must-revalidate");
3540
	}
3541

    
3542
	/* Ensure output buffering is off so PHP does not consume
3543
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3544
	while (ob_get_level()) {
3545
		@ob_end_clean();
3546
	}
3547

    
3548
	/* Send the data to the user */
3549
	if ($type == 'file') {
3550
		readfile($content);
3551
	} else {
3552
		echo $content;
3553
	}
3554

    
3555
	/* Flush any remaining output buffer */
3556
	@ob_end_flush();
3557
	exit;
3558
}
3559

    
3560
// Test whether the hostname in a URL can be resolved with a very short timeout
3561
function is_url_hostname_resolvable($url) {
3562
	$urlhostname = parse_url($url, PHP_URL_HOST);
3563
	if (empty($urlhostname)) {
3564
		return false;
3565
	}
3566
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3567
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3568
	putenv("RES_OPTIONS");
3569
	return $resolvable;
3570
}
3571

    
3572
function get_pf_timeouts () {
3573
	$pftimeout = array();
3574
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3575
	if ($retval == 0) {
3576
		foreach ($pfctlst as $pfst) {
3577
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3578
			if ($timeout[1] == "other") {
3579
				$proto = "Other";
3580
			} else {
3581
				$proto = strtoupper($timeout[1]);
3582
			}
3583
			if ($timeout[2] == "finwait") {
3584
				$type = "FIN Wait";
3585
			} else {
3586
				$type = ucfirst($timeout[2]);
3587
			}
3588
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3589
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3590
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3591
		}
3592
	}
3593
	return $pftimeout;
3594
}
3595

    
3596
function set_curlproxy(&$ch) {
3597
	global $config;
3598

    
3599
	if (!empty($config['system']['proxyurl'])) {
3600
		curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
3601
		if (!empty($config['system']['proxyport'])) {
3602
			curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
3603
		}
3604
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
3605
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3606
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
3607
		}
3608
	}
3609
}
3610

    
3611
?>
(38-38/61)