Project

General

Profile

Download (104 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-2023 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
require_once("config.inc");
25
require_once("config.lib.inc");
26

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

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

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

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

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

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

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

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

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

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

    
165
	if (is_array($cssfiles)) {
166
		arsort($cssfiles);
167
		$usrcss = $pfscss = $betacss = array();
168

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

    
183
		$css = array_merge($pfscss, $betacss, $usrcss);
184

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

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

    
204
	$csslist = get_css_files();
205

    
206
	if (!isset($csslist[$value])) {
207
		$value = "pfSense.css";
208
	}
209

    
210
	$section->addInput(new Form_Select(
211
		'webguicss',
212
		'Theme',
213
		$value,
214
		$csslist
215
	))->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>');
216
}
217
function validate_webguicss_field(&$input_errors, $value) {
218
	$csslist = get_css_files();
219
	if (!isset($csslist[$value])) {
220
		$input_errors[] = gettext("The submitted Theme could not be found. Pick a different theme.");
221
	}
222
}
223

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

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

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

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

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

    
285
	if (((int) $value < 1) || ((int) $value > 6)) {
286
		$value = 2;
287
	}
288

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

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

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

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

    
333
	$group = new Form_Group('Associated Panels Show/Hide');
334

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

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

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

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

    
363
	$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.');
364

    
365
	$section->add($group);
366
}
367

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

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

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

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

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

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

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

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

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

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

    
488
	if ($has_created_time || $has_updated_time) {
489
		$section = new Form_Section('Rule Information');
490

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

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

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

    
518
		$form->add($section);
519
	}
520
}
521

    
522
function hardware_offloading_applyflags($iface) {
523
	$flags_on = 0;
524
	$flags_off = 0;
525
	$options = get_interface_addresses($iface);
526

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

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

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

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

    
576
/****f* pfsense-utils/enable_hardware_offloading
577
 * NAME
578
 *   enable_hardware_offloading - Enable a NIC's supported hardware features.
579
 * INPUTS
580
 *   $interface	- string containing the physical interface to work on.
581
 * RESULT
582
 *   null
583
 * NOTES
584
 *   This function only supports the fxp driver's loadable microcode.
585
 ******/
586
function enable_hardware_offloading($interface) {
587
	$int = get_real_interface($interface);
588
	if (empty($int)) {
589
		return;
590
	}
591

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

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

    
613
/****f* pfsense-utils/is_alias_inuse
614
 * NAME
615
 *   checks to see if an alias is currently in use by a rule
616
 * INPUTS
617
 *
618
 * RESULT
619
 *   true or false
620
 * NOTES
621
 *
622
 ******/
623
function is_alias_inuse($alias) {
624
	if ($alias == "") {
625
		return false;
626
	}
627
	/* loop through firewall rules looking for alias in use */
628
	foreach (config_get_path('filter/rule', []) as $rule) {
629
		foreach (['source', 'destination'] as $origin) {
630
			if (array_get_path($rule, "{$origin}/address") == $alias) {
631
				return true;
632
			}
633
		}
634
	}
635
	/* loop through nat rules looking for alias in use */
636
	foreach (config_get_path('nat/rule', []) as $rule) {
637
		foreach (['target', 'source/address', 'destination/address'] as $property) {
638
			if (array_get_path($rule, $property) == $alias) {
639
				return true;
640
			}
641
		}
642
	}
643
	return false;
644
}
645

    
646
/****f* pfsense-utils/is_schedule_inuse
647
 * NAME
648
 *   checks to see if a schedule is currently in use by a rule
649
 * INPUTS
650
 *
651
 * RESULT
652
 *   true or false
653
 * NOTES
654
 *
655
 ******/
656
function is_schedule_inuse($schedule) {
657
	if ($schedule == "") {
658
		return false;
659
	}
660
	/* loop through firewall rules looking for schedule in use */
661
	foreach (config_get_path('filter/rule', []) as $rule) {
662
		if ($rule['sched'] == $schedule) {
663
			return true;
664
		}
665
	}
666
	return false;
667
}
668

    
669
/****f* pfsense-utils/setup_microcode
670
 * NAME
671
 *   enumerates all interfaces and calls enable_hardware_offloading which
672
 *   enables a NIC's supported hardware features.
673
 * INPUTS
674
 *
675
 * RESULT
676
 *   null
677
 * NOTES
678
 *   This function only supports the fxp driver's loadable microcode.
679
 ******/
680
function setup_microcode() {
681

    
682
	/* if list */
683
	$iflist = get_configured_interface_list(true);
684
	foreach (array_keys($iflist) as $if) {
685
		enable_hardware_offloading($if);
686
	}
687
	unset($iflist);
688
}
689

    
690
/****f* pfsense-utils/get_carp_status
691
 * NAME
692
 *   get_carp_status - Return whether CARP is enabled or disabled.
693
 * RESULT
694
 *   boolean	- true if CARP is enabled, false if otherwise.
695
 ******/
696
function get_carp_status() {
697
	/* grab the current status of carp */
698
	$status = get_single_sysctl('net.inet.carp.allow');
699
	return (intval($status) > 0);
700
}
701

    
702
/**
703
 * Enable or disable CARP by toggling net.inet.carp.allow if necessary, enabling
704
 * only if CARP VIPs are configured
705
 */
706
function enable_carp(bool $enable=true) {
707
	$carp_enabled = get_carp_status();
708
	$carplist = get_configured_vip_list('all', VIP_CARP);
709
	if (($enable != $carp_enabled) && (!$enable || count($carplist) > 0)) {
710
		set_single_sysctl("net.inet.carp.allow", $enable?"1":"0");
711
	}
712
}
713
	
714
/*
715
 * convert_ip_to_network_format($ip, $subnet): converts an ip address to network form
716
 */
717
function convert_ip_to_network_format($ip, $subnet) {
718
	$ipsplit = explode('.', $ip);
719
	$string = $ipsplit[0] . "." . $ipsplit[1] . "." . $ipsplit[2] . ".0/" . $subnet;
720
	return $string;
721
}
722

    
723
/*
724
 * get_carp_interface_status($carpid): returns the status of a carp uniqid
725
 */
726
function get_carp_interface_status($carpid) {
727

    
728
	$carpiface = get_configured_vip_interface($carpid);
729
	if ($carpiface == NULL)
730
		return "";
731
	$interface = get_real_interface($carpiface);
732
	if ($interface == NULL)
733
		return "";
734
	$vip = get_configured_vip($carpid);
735
	if ($vip == NULL || !isset($vip['vhid']))
736
		return "";
737

    
738
	$vhid = $vip['vhid'];
739
	$carp_query = [];
740
	exec("/sbin/ifconfig {$interface} | /usr/bin/grep \"carp:.* vhid {$vhid} \"", $carp_query);
741
	foreach ($carp_query as $int) {
742
		if (stripos($int, "MASTER"))
743
			return "MASTER";
744
		elseif (stripos($int, "BACKUP"))
745
			return "BACKUP";
746
		elseif (stripos($int, "INIT"))
747
			return "INIT";
748
	}
749

    
750
	return "";
751
}
752

    
753
function get_carp_bind_status($interface) {
754
	$carpstatus = get_carp_interface_status($interface);
755
	if (!empty($carpstatus)) {
756
		return $carpstatus;
757
	} else {
758
		foreach (config_get_path('virtualip/vip', []) as $vip) {
759
			if ($interface == "_vip{$vip['uniqid']}") { 
760
				return get_carp_interface_status($vip['interface']);
761
			}
762
		}
763
	}
764
}
765

    
766
/*
767
 * Return true if the CARP status of at least one interface of a captive portal zone is in backup mode
768
 * This function return false if CARP is not enabled on any interface of the captive portal zone
769
 */
770
function captiveportal_ha_is_node_in_backup_mode($cpzone) {
771
	$cpinterfaces = explode(",", config_get_path("captiveportal/{$cpzone}/interface", ""));
772

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

    
785
/****f* pfsense-utils/WakeOnLan
786
 * NAME
787
 *   WakeOnLan - Wake a machine up using the wake on lan format/protocol
788
 * RESULT
789
 *   true/false - true if the operation was successful
790
 ******/
791
function WakeOnLan($addr, $mac) {
792
	$addr_byte = explode(':', $mac);
793
	$hw_addr = '';
794

    
795
	for ($a = 0; $a < 6; $a++) {
796
		$hw_addr .= chr(hexdec($addr_byte[$a]));
797
	}
798

    
799
	$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
800

    
801
	for ($a = 1; $a <= 16; $a++) {
802
		$msg .= $hw_addr;
803
	}
804

    
805
	// send it to the broadcast address using UDP
806
	$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
807
	if ($s == false) {
808
		log_error(gettext("Error creating socket!"));
809
		log_error(sprintf(gettext("Error code is '%1\$s' - %2\$s"), socket_last_error(), socket_strerror(socket_last_error())));
810
	} else {
811
		// setting a broadcast option to socket:
812
		$opt_ret = socket_set_option($s, 1, 6, TRUE);
813
		if ($opt_ret < 0) {
814
			log_error(sprintf(gettext("setsockopt() failed, error: %s"),
815
							  socket_strerror(socket_last_error($s))));
816
		}
817
		$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
818
		socket_close($s);
819
		log_error(sprintf(gettext('Magic Packet sent (%1$s) to (%2$s) MAC=%3$s'), $e, $addr, $mac));
820
		return true;
821
	}
822

    
823
	return false;
824
}
825

    
826
/*
827
 * reverse_strrchr($haystack, $needle):  Return everything in $haystack up to the *last* instance of $needle.
828
 *					 Useful for finding paths and stripping file extensions.
829
 */
830
function reverse_strrchr($haystack, $needle) {
831
	if (!is_string($haystack)) {
832
		return;
833
	}
834
	return strrpos($haystack, $needle) ? substr($haystack, 0, strrpos($haystack, $needle) +1) : false;
835
}
836

    
837
/*
838
 *  backup_config_section($section): returns as an xml file string of
839
 *                                   the configuration section
840
 */
841
function backup_config_section($section_name) {
842
	$new_section = config_get_path($section_name, []);
843
	/* generate configuration XML */
844
	$xmlconfig = dump_xml_config($new_section, $section_name);
845
	$xmlconfig = str_replace("<?xml version=\"1.0\"?>", "", $xmlconfig);
846
	return $xmlconfig;
847
}
848

    
849
/*
850
 *  restore_config_section($section_name, new_contents): restore a configuration section,
851
 *                                                  and write the configuration out
852
 *                                                  to disk/cf.
853
 */
854
function restore_config_section($section_name, $new_contents) {
855
	global $g;
856
	$fout = fopen("{$g['tmp_path']}/tmpxml", "w");
857
	fwrite($fout, $new_contents);
858
	fclose($fout);
859

    
860
	$xml = parse_xml_config(g_get('tmp_path') . "/tmpxml", null);
861
	if ($xml['pfsense']) {
862
		$xml = $xml['pfsense'];
863
	}
864
	if ($xml[$section_name]) {
865
		$section_xml = $xml[$section_name];
866
	} else {
867
		$section_xml = -1;
868
	}
869

    
870
	@unlink(g_get('tmp_path') . "/tmpxml");
871
	if ($section_xml === -1) {
872
		return false;
873
	}
874

    
875
	/* Save current pkg repo to re-add on new config */
876
	unset($pkg_repo_conf_path);
877
	if ($section_name == "system" &&
878
	    config_get_path('system/pkg_repo_conf_path')) {
879
		$pkg_repo_conf_path = config_get_path('system/pkg_repo_conf_path');
880
	}
881

    
882
	config_set_path($section_name, $section_xml);
883
	if (file_exists("{$g['tmp_path']}/config.cache")) {
884
		unlink("{$g['tmp_path']}/config.cache");
885
	}
886

    
887
	/* Restore previously pkg repo configured */
888
	if ($section_name == "system") {
889
		if (isset($pkg_repo_conf_path)) {
890
			config_set_path('system/pkg_repo_conf_path', $pkg_repo_conf_path);
891
		} elseif (config_get_path('system/pkg_repo_conf_path')) {
892
			config_del_path(('system/pkg_repo_conf_path'));
893
		}
894
	}
895

    
896
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
897
	disable_security_checks();
898
	return true;
899
}
900

    
901
/*
902
 *  merge_config_section($section_name, new_contents):   restore a configuration section,
903
 *                                                  and write the configuration out
904
 *                                                  to disk/cf.  But preserve the prior
905
 * 													structure if needed
906
 */
907
function merge_config_section($section_name, $new_contents) {
908
	$fname = get_tmp_file();
909
	$fout = fopen($fname, "w");
910
	fwrite($fout, $new_contents);
911
	fclose($fout);
912
	$section_xml = parse_xml_config($fname, $section_name);
913
	config_set_path($section_name, $section_xml);
914
	unlink($fname);
915
	write_config(sprintf(gettext("Restored %s of config file (maybe from CARP partner)"), $section_name));
916
	disable_security_checks();
917
	return;
918
}
919

    
920
/*
921
 * rmdir_recursive($path, $follow_links=false)
922
 * Recursively remove a directory tree (rm -rf path)
923
 * This is for directories _only_
924
 */
925
function rmdir_recursive($path, $follow_links=false) {
926
	$to_do = glob($path);
927
	if (!is_array($to_do)) {
928
		$to_do = array($to_do);
929
	}
930
	foreach ($to_do as $workingdir) { // Handle wildcards by foreaching.
931
		if (file_exists($workingdir)) {
932
			if (is_dir($workingdir)) {
933
				$dir = opendir($workingdir);
934
				while ($entry = readdir($dir)) {
935
					if (is_file("$workingdir/$entry") || ((!$follow_links) && is_link("$workingdir/$entry"))) {
936
						unlink("$workingdir/$entry");
937
					} elseif (is_dir("$workingdir/$entry") && $entry != '.' && $entry != '..') {
938
						rmdir_recursive("$workingdir/$entry");
939
					}
940
				}
941
				closedir($dir);
942
				rmdir($workingdir);
943
			} elseif (is_file($workingdir)) {
944
				unlink($workingdir);
945
			}
946
		}
947
	}
948
	return;
949
}
950

    
951
/*
952
 * host_firmware_version(): Return the versions used in this install
953
 */
954
function host_firmware_version() {
955
	global $g;
956

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

    
959
	return array(
960
		"firmware" => array("version" => g_get('product_version')),
961
		"kernel"   => array("version" => $os_version),
962
		"base"     => array("version" => $os_version),
963
		"platform" => g_get('product_label'),
964
		"config_version" => config_get_path('version')
965
	);
966
}
967

    
968
function get_disk_info() {
969
	$diskout = "";
970
	exec("/bin/df -h | /usr/bin/grep -w '/' | /usr/bin/awk '{ print $2, $3, $4, $5 }'", $diskout);
971
	return explode(' ', $diskout[0]);
972
}
973

    
974
/****f* pfsense-utils/strncpy
975
 * NAME
976
 *   strncpy - copy strings
977
 * INPUTS
978
 *   &$dst, $src, $length
979
 * RESULT
980
 *   none
981
 ******/
982
function strncpy(&$dst, $src, $length) {
983
	if (strlen($src) > $length) {
984
		$dst = substr($src, 0, $length);
985
	} else {
986
		$dst = $src;
987
	}
988
}
989

    
990
/****f* pfsense-utils/reload_interfaces_sync
991
 * NAME
992
 *   reload_interfaces - reload all interfaces
993
 * INPUTS
994
 *   none
995
 * RESULT
996
 *   none
997
 ******/
998
function reload_interfaces_sync() {
999
	global $config, $g;
1000

    
1001
	if (g_get('debug')) {
1002
		log_error(gettext("reload_interfaces_sync() is starting."));
1003
	}
1004

    
1005
	/* parse config.xml again */
1006
	$config = parse_config(true);
1007

    
1008
	/* enable routing */
1009
	system_routing_enable();
1010
	if (g_get('debug')) {
1011
		log_error(gettext("Enabling system routing"));
1012
	}
1013

    
1014
	if (g_get('debug')) {
1015
		log_error(gettext("Cleaning up Interfaces"));
1016
	}
1017

    
1018
	/* set up interfaces */
1019
	interfaces_configure();
1020
}
1021

    
1022
/****f* pfsense-utils/reload_all
1023
 * NAME
1024
 *   reload_all - triggers a reload of all settings
1025
 *   * INPUTS
1026
 *   none
1027
 * RESULT
1028
 *   none
1029
 ******/
1030
function reload_all() {
1031
	send_event("service reload all");
1032
}
1033

    
1034
/****f* pfsense-utils/reload_interfaces
1035
 * NAME
1036
 *   reload_interfaces - triggers a reload of all interfaces
1037
 * INPUTS
1038
 *   none
1039
 * RESULT
1040
 *   none
1041
 ******/
1042
function reload_interfaces() {
1043
	send_event("interface all reload");
1044
}
1045

    
1046
/****f* pfsense-utils/reload_all_sync
1047
 * NAME
1048
 *   reload_all - reload all settings
1049
 *   * INPUTS
1050
 *   none
1051
 * RESULT
1052
 *   none
1053
 ******/
1054
function reload_all_sync() {
1055
	global $config;
1056

    
1057
	/* parse config.xml again */
1058
	$config = parse_config(true);
1059

    
1060
	/* set up our timezone */
1061
	system_timezone_configure();
1062

    
1063
	/* set up our hostname */
1064
	system_hostname_configure();
1065

    
1066
	/* make hosts file */
1067
	system_hosts_generate();
1068

    
1069
	/* generate resolv.conf */
1070
	system_resolvconf_generate();
1071

    
1072
	/* enable routing */
1073
	system_routing_enable();
1074

    
1075
	/* set up interfaces */
1076
	interfaces_configure();
1077

    
1078
	/* start dyndns service */
1079
	services_dyndns_configure();
1080

    
1081
	/* configure cron service */
1082
	configure_cron();
1083

    
1084
	/* start the NTP client */
1085
	system_ntp_configure();
1086

    
1087
	/* sync pw database */
1088
	unlink_if_exists("/etc/spwd.db.tmp");
1089
	mwexec("/usr/sbin/pwd_mkdb -d /etc/ /etc/master.passwd");
1090

    
1091
	/* restart sshd */
1092
	send_event("service restart sshd");
1093

    
1094
	/* restart webConfigurator if needed */
1095
	send_event("service restart webgui");
1096
}
1097

    
1098
function load_loader_conf($loader_conf = NULL, $local = false) {
1099

    
1100
	if ($loader_conf == NULL) {
1101
		return (NULL);
1102
	}
1103
	if (file_exists($loader_conf)) {
1104
		$input = file_get_contents($loader_conf);
1105
	} else {
1106
		$input = "";
1107
	}
1108

    
1109
	$input_split = explode("\n", $input);
1110

    
1111
	/*
1112
	 * Loop through and only add lines that are not empty and not
1113
	 * managed by us.
1114
	 */
1115
	$data = array();
1116
	/* These values should be removed from loader.conf and loader.conf.local
1117
	 * As they will be replaced when necessary. */
1118
	$remove = array(
1119
	    "hint.cordbuc.0",
1120
	    "hint.e6000sw.0",
1121
	    "hint.gpioled",
1122
	    "hint.mdio.0.at",
1123
	    "hint-model.",
1124
	    "hw.e6000sw.default_disabled",
1125
	    "hw.hn.vf_transparent",
1126
	    "hw.hn.use_if_start",
1127
	    "hw.usb.no_pf",
1128
	    "net.pf.request_maxcount",
1129
	    "vm.pmap.pti",
1130
	);
1131
	if (!$local) {
1132
		/* These values should only be filtered in loader.conf, not .local */
1133
		$remove = array_merge($remove, array(
1134
		    "autoboot_delay",
1135
		    "boot_multicons",
1136
		    "boot_serial",
1137
		    "comconsole_speed",
1138
		    "comconsole_port",
1139
		    "console",
1140
		    "debug.ddb.capture.bufsize",
1141
		    "hint.uart.0.flags",
1142
		    "hint.uart.1.flags",
1143
		    "net.link.ifqmaxlen"
1144
		));
1145
	}
1146
	foreach ($input_split as $line) {
1147
		if (empty($line)) {
1148
			continue;
1149
		}
1150
		$skip = false;
1151
		$name = explode('=', $line, 2)[0];
1152
		foreach($remove as $rkey) {
1153
			if (strncasecmp(trim($name), $rkey, strlen($rkey)) == 0) {
1154
				$skip = true;
1155
				break;
1156
			}
1157
		}
1158
		if (!$skip) {
1159
			$data[] = $line;
1160
		}
1161
	}
1162

    
1163
	return ($data);
1164
}
1165

    
1166
function setup_loader_settings($path = "", $upgrade = false) {
1167
	global $g;
1168

    
1169
	$boot_config_file = "{$path}/boot.config";
1170
	$loader_conf_file = "{$path}/boot/loader.conf";
1171

    
1172
	$serialspeed = (config_get_path('system/serialspeed', 115200));
1173

    
1174
	$vga_only = false;
1175
	$serial_only = false;
1176
	$specific_platform = system_identify_specific_platform();
1177
	$video_console_type = (get_single_sysctl("machdep.bootmethod") == "UEFI") ? "efi" : "vidconsole";
1178
	if ($specific_platform['name'] == '1540' ||
1179
	    $specific_platform['name'] == '1541') {
1180
		$vga_only = true;
1181
	} elseif ($specific_platform['name'] == 'Turbot Dual-E') {
1182
		$g['primaryconsole_force'] = "video";
1183
	} elseif ($specific_platform['name'] == 'RCC-VE' ||
1184
	    $specific_platform['name'] == 'RCC' ||
1185
	    $specific_platform['name'] == 'SG-2220' ||
1186
	    $specific_platform['name'] == 'apu2') {
1187
		$serial_only = true;
1188
	}
1189

    
1190
	/* Serial console - write out /boot.config */
1191
	if (file_exists($boot_config_file)) {
1192
		$boot_config = file_get_contents($boot_config_file);
1193
	} else {
1194
		$boot_config = "";
1195
	}
1196
	$boot_config_split = explode("\n", $boot_config);
1197
	$data = array();
1198
	foreach ($boot_config_split as $bcs) {
1199
		/* Ignore -S, -D and -h lines now */
1200
		if (!empty($bcs) && !strstr($bcs, "-S") &&
1201
		    !strstr($bcs, "-D") && !strstr($bcs, "-h")) {
1202
			$data[] = $bcs;
1203
		}
1204
	}
1205
	if ($serial_only === true) {
1206
		$data[] = "-S{$serialspeed} -h";
1207
	} elseif (is_serial_enabled()) {
1208
		$data[] = "-S{$serialspeed} -D";
1209
	}
1210

    
1211
	if (empty($data)) {
1212
		@unlink($boot_config_file);
1213
	} else {
1214
		safe_write_file($boot_config_file, $data);
1215
	}
1216
	unset($data, $boot_config, $boot_config_file, $boot_config_split);
1217

    
1218
	/* Serial console - write out /boot/loader.conf */
1219
	if ($upgrade) {
1220
		system("echo \"Reading {$loader_conf_file}...\" >> /conf/upgrade_log.txt");
1221
	}
1222

    
1223
	$data = load_loader_conf($loader_conf_file, false);
1224
	if ($serial_only === true) {
1225
		$data[] = 'boot_serial="YES"';
1226
		$data[] = 'console="comconsole"';
1227
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1228
	} elseif ($vga_only === true) {
1229
		if ($video_console_type == 'efi') {
1230
			$data[] = 'boot_serial="NO"';
1231
		}
1232
		$data[] = "console=\"{$video_console_type}\"";
1233
	} elseif (is_serial_enabled()) {
1234
		$data[] = 'boot_multicons="YES"';
1235
		$primaryconsole = isset($g['primaryconsole_force']) ?
1236
		    g_get('primaryconsole_force') :
1237
		    config_get_path('system/primaryconsole');
1238
		switch ($primaryconsole) {
1239
			case "video":
1240
				if ($video_console_type == 'efi') {
1241
					$data[] = 'boot_serial="NO"';
1242
				}
1243
				$data[] = "console=\"{$video_console_type},comconsole\"";
1244
				break;
1245
			case "serial":
1246
			default:
1247
				$data[] = 'boot_serial="YES"';
1248
				$data[] = "console=\"comconsole,{$video_console_type}\"";
1249
		}
1250
		$data[] = 'comconsole_speed="' . $serialspeed . '"';
1251
	} elseif ($video_console_type == 'efi') {
1252
		$data[] = 'boot_serial="NO"';
1253
	}
1254

    
1255
	if ($specific_platform['name'] == 'RCC-VE' ||
1256
	    $specific_platform['name'] == 'RCC' ||
1257
	    $specific_platform['name'] == 'SG-2220') {
1258
		$data[] = 'comconsole_port="0x2F8"';
1259
		$data[] = 'hint.uart.0.flags="0x00"';
1260
		$data[] = 'hint.uart.1.flags="0x10"';
1261
	}
1262
	$data[] = 'autoboot_delay="3"';
1263
	if (config_path_enabled('system','pti_disabled')) {
1264
		$data[] = 'vm.pmap.pti="0"';
1265
	}
1266

    
1267
	/* Enable ALTQ support for hnX NICs. */
1268
	if (config_path_enabled('system','hn_altq_enable')) {
1269
		$data[] = 'hw.hn.vf_transparent="0"';
1270
		$data[] = 'hw.hn.use_if_start="1"';
1271
	}
1272

    
1273
	/* Set maximum send queue length. */
1274
	$data[] = 'net.link.ifqmaxlen="128"';
1275

    
1276
	safe_write_file($loader_conf_file, $data);
1277

    
1278
	/* Filter loader.conf.local to avoid duplicate settings. */
1279
	$loader_conf_file = "{$path}/boot/loader.conf.local";
1280
	$data = load_loader_conf($loader_conf_file, true);
1281
	if (empty($data)) {
1282
		@unlink($loader_conf_file);
1283
	} else {
1284
		safe_write_file($loader_conf_file, $data);
1285
	}
1286

    
1287
}
1288

    
1289
function console_configure($when = "save", $path = "") {
1290
	$ttys_file = "{$path}/etc/ttys";
1291

    
1292
	/* Update the loader settings. */
1293
	setup_loader_settings($path, ($when == "upgrade"));
1294

    
1295
	$ttys = file_get_contents($ttys_file);
1296
	$ttys_split = explode("\n", $ttys);
1297

    
1298
	$data = array();
1299

    
1300
	$on_off = (is_serial_enabled() ? 'onifconsole' : 'off');
1301

    
1302
	if (config_path_enabled('system','disableconsolemenu')) {
1303
		$console_type = 'Pc';
1304
		$serial_type = '3wire';
1305
	} else {
1306
		$console_type = 'al.Pc';
1307
		$serial_type = 'al.3wire';
1308
	}
1309

    
1310
	$console_line = "console\tnone\t\t\t\tunknown\toff\tsecure";
1311
	$virt_line =
1312
	    "\"/usr/libexec/getty {$console_type}\"\txterm\tonifexists secure";
1313
	$ttyu_line =
1314
	    "\"/usr/libexec/getty {$serial_type}\"\tvt100\t{$on_off}\tsecure";
1315

    
1316
	$found = array();
1317

    
1318
	foreach ($ttys_split as $tty) {
1319
		/* Ignore blank lines */
1320
		if (empty($tty)) {
1321
			continue;
1322
		}
1323

    
1324
		if (stristr($tty, "ttyv0")) {
1325
			$found['ttyv0'] = 1;
1326
			$data[] = "ttyv0\t{$virt_line}";
1327
		} elseif (stristr($tty, "xc0")) {
1328
			$found['xc0'] = 1;
1329
			$data[] = "xc0\t{$virt_line}";
1330
		} elseif (stristr($tty, "ttyu")) {
1331
			$ttyn = substr($tty, 0, 5);
1332
			$found[$ttyn] = 1;
1333
			$data[] = "{$ttyn}\t{$ttyu_line}";
1334
		} elseif (substr($tty, 0, 7) == 'console') {
1335
			$found['console'] = 1;
1336
			$data[] = $tty;
1337
		} else {
1338
			$data[] = $tty;
1339
		}
1340
	}
1341
	unset($on_off, $console_type, $serial_type);
1342

    
1343
	/* Detect missing main lines on original file and try to rebuild it */
1344
	$items = array(
1345
		'console',
1346
		'ttyv0',
1347
		'ttyu0',
1348
		'ttyu1',
1349
		'ttyu2',
1350
		'ttyu3',
1351
		'xc0'
1352
	);
1353

    
1354
	foreach ($items as $item) {
1355
		if (isset($found[$item])) {
1356
			continue;
1357
		}
1358

    
1359
		if ($item == 'console') {
1360
			$data[] = $console_line;
1361
		} elseif (($item == 'ttyv0') || ($item == 'xc0')) {
1362
			/* xc0 - Xen console, see https://redmine.pfsense.org/issues/11402 */
1363
			$data[] = "{$item}\t{$virt_line}";
1364
		} else {
1365
			$data[] = "{$item}\t{$ttyu_line}";
1366
		}
1367
	}
1368

    
1369
	safe_write_file($ttys_file, $data);
1370

    
1371
	unset($ttys, $ttys_file, $ttys_split, $data);
1372

    
1373
	if ($when != "upgrade") {
1374
		reload_ttys();
1375
	}
1376

    
1377
	return;
1378
}
1379

    
1380
function is_serial_enabled() {
1381
	global $g;
1382

    
1383
	if (!isset($g['enableserial_force']) &&
1384
	    !config_path_enabled('system','enableserial')) {
1385
		return false;
1386
	}
1387

    
1388
	return true;
1389
}
1390

    
1391
function reload_ttys() {
1392
	// Send a HUP signal to init will make it reload /etc/ttys
1393
	posix_kill(1, SIGHUP);
1394
}
1395

    
1396
function print_value_list($list, $count = 10, $separator = ",") {
1397
	$ret = implode($separator, array_slice($list, 0, $count));
1398
	if (count($list) < $count) {
1399
		$ret .= ".";
1400
	} else {
1401
		$ret .= "...";
1402
	}
1403
	return $ret;
1404
}
1405

    
1406
/* DHCP enabled on any interfaces? */
1407
function is_dhcp_server_enabled() {
1408
	foreach (config_get_path('dhcpd', []) as $dhcpif => $dhcpifconf) {
1409
		if (empty($dhcpifconf)) {
1410
			continue;
1411
		}
1412
		if (isset($dhcpifconf['enable']) &&
1413
			!empty(config_get_path("interfaces/{$dhcpif}"))) {
1414
			return true;
1415
		}
1416
	}
1417

    
1418
	return false;
1419
}
1420

    
1421
/* DHCP enabled on any interfaces? */
1422
function is_dhcpv6_server_enabled() {
1423
	foreach (config_get_path('interfaces', []) as $ifcfg) {
1424
		if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface'])) {
1425
			return true;
1426
		}
1427
	}
1428

    
1429
	foreach (config_get_path('dhcpdv6', []) as $dhcpv6if => $dhcpv6ifconf) {
1430
		if (empty($dhcpv6ifconf)) {
1431
			continue;
1432
		}
1433
		if (isset($dhcpv6ifconf['enable']) &&
1434
			!empty(config_get_path("interfaces/{$dhcpv6if}"))) {
1435
			return true;
1436
		}
1437
	}
1438

    
1439
	return false;
1440
}
1441

    
1442
/* radvd enabled on any interfaces? */
1443
function is_radvd_enabled() {
1444
	init_config_arr(['dhcpdv6']);
1445
	$dhcpdv6cfg = config_get_path('dhcpdv6', []);
1446
	$Iflist = get_configured_interface_list();
1447

    
1448
	/* handle manually configured DHCP6 server settings first */
1449
	foreach ($dhcpdv6cfg as $dhcpv6if => $dhcpv6ifconf) {
1450
		if (empty($dhcpv6ifconf)) {
1451
			continue;
1452
		}
1453
		if (!config_path_enabled("interfaces/{$dhcpv6if}")) {
1454
			continue;
1455
		}
1456

    
1457
		if (!isset($dhcpv6ifconf['ramode'])) {
1458
			$dhcpv6ifconf['ramode'] = $dhcpv6ifconf['mode'];
1459
		}
1460

    
1461
		if ($dhcpv6ifconf['ramode'] == "disabled") {
1462
			continue;
1463
		}
1464

    
1465
		$ifcfgipv6 = get_interface_ipv6($dhcpv6if);
1466
		if (!is_ipaddrv6($ifcfgipv6)) {
1467
			continue;
1468
		}
1469

    
1470
		return true;
1471
	}
1472

    
1473
	/* handle DHCP-PD prefixes and 6RD dynamic interfaces */
1474
	foreach (array_keys($Iflist) as $if) {
1475
		if (!config_path_enabled("interfaces/{$if}", 'track6-interface')) {
1476
			continue;
1477
		}
1478
		if (!config_path_enabled("interfaces/{$if}")) {
1479
			continue;
1480
		}
1481

    
1482
		$ifcfgipv6 = get_interface_ipv6($if);
1483
		if (!is_ipaddrv6($ifcfgipv6)) {
1484
			continue;
1485
		}
1486

    
1487
		$ifcfgsnv6 = get_interface_subnetv6($if);
1488
		$subnetv6 = gen_subnetv6($ifcfgipv6, $ifcfgsnv6);
1489

    
1490
		if (!is_ipaddrv6($subnetv6)) {
1491
			continue;
1492
		}
1493

    
1494
		return true;
1495
	}
1496

    
1497
	return false;
1498
}
1499

    
1500
/* Any PPPoE servers enabled? */
1501
function is_pppoe_server_enabled() {
1502
	$pppoeenable = false;
1503

    
1504
	foreach (config_get_path('pppoes/pppoe', []) as $pppoes) {
1505
		if ($pppoes['mode'] == 'server') {
1506
			$pppoeenable = true;
1507
		}
1508
	}
1509

    
1510
	return $pppoeenable;
1511
}
1512

    
1513
/* Optional arg forces hh:mm:ss without days */
1514
function convert_seconds_to_dhms($sec, $showhoursonly = false) {
1515
	if (!is_numericint($sec)) {
1516
		return '-';
1517
	}
1518
	// FIXME: When we move to PHP 7 we can use "intdiv($sec % X, Y)" etc
1519
	list($d, $h, $m, $s) = array(	(int)($showhoursonly ? 0 : $sec/86400),
1520
					(int)(($showhoursonly ? $sec : $sec % 86400)/3600),
1521
					(int)(($sec % 3600)/60),
1522
					$sec % 60
1523
				);
1524
	return ($d > 0 ? $d . 'd ' : '') . sprintf('%02d:%02d:%02d', $h, $m, $s);
1525
}
1526

    
1527
/* Compute the total uptime from the ppp uptime log file in the conf directory */
1528

    
1529
function get_ppp_uptime($port) {
1530
	if (file_exists("/conf/{$port}.log")) {
1531
		$saved_time = file_get_contents("/conf/{$port}.log");
1532
		$uptime_data = explode("\n", $saved_time);
1533
		$sec = 0;
1534
		foreach ($uptime_data as $upt) {
1535
			/* Skip blank lines, trim, and cast to int to ensure type matches.
1536
			 * https://redmine.pfsense.org/issues/14117 */
1537
			if (!empty($upt)) {
1538
				$sec += (int) trim(substr($upt, 1 + strpos($upt, " ")));
1539
			}
1540
		}
1541
		return convert_seconds_to_dhms($sec);
1542
	} else {
1543
		$total_time = gettext("No history data found!");
1544
		return $total_time;
1545
	}
1546
}
1547

    
1548
//returns interface information
1549
function get_interface_info($ifdescr) {
1550
	global $g;
1551

    
1552
	$ifinfo = array();
1553
	if (empty(config_get_path("interfaces/{$ifdescr}"))) {
1554
		return;
1555
	}
1556
	$ifinfo['hwif'] = config_get_path("interfaces/{$ifdescr}/if");
1557
	$ifinfo['enable'] = config_path_enabled("interfaces/{$ifdescr}");
1558
	$ifinfo['if'] = get_real_interface($ifdescr);
1559

    
1560
	$chkif = $ifinfo['if'];
1561
	$ifinfotmp = get_interface_addresses($chkif);
1562
	$ifinfo['status'] = $ifinfotmp['status'];
1563
	if (empty($ifinfo['status'])) {
1564
		$ifinfo['status'] = "down";
1565
	}
1566
	$ifinfo['macaddr'] = $ifinfotmp['macaddr'];
1567
	$ifinfo['mtu'] = $ifinfotmp['mtu'];
1568
	$ifinfo['ipaddr'] = $ifinfotmp['ipaddr'];
1569
	$ifinfo['subnet'] = $ifinfotmp['subnet'];
1570
	$ifinfo['linklocal'] = get_interface_linklocal($ifdescr);
1571
	$ifinfo['ipaddrv6'] = get_interface_ipv6($ifdescr);
1572
	$ifinfo['subnetv6'] = get_interface_subnetv6($ifdescr);
1573
	if (isset($ifinfotmp['link0'])) {
1574
		$link0 = "down";
1575
	}
1576
	$ifinfotmp = pfSense_get_interface_stats($chkif);
1577
	// $ifinfo['inpkts'] = $ifinfotmp['inpkts'];
1578
	// $ifinfo['outpkts'] = $ifinfotmp['outpkts'];
1579
	$ifinfo['inerrs'] = $ifinfotmp['inerrs'];
1580
	$ifinfo['outerrs'] = $ifinfotmp['outerrs'];
1581
	$ifinfo['collisions'] = $ifinfotmp['collisions'];
1582

    
1583
	/* Use pfctl for non wrapping 64 bit counters */
1584
	/* Pass */
1585
	exec("/sbin/pfctl -vvsI -i {$chkif}", $pfctlstats);
1586
	$pf_in4_pass = preg_split("/ +/ ", $pfctlstats[3]);
1587
	$pf_out4_pass = preg_split("/ +/", $pfctlstats[5]);
1588
	$pf_in6_pass = preg_split("/ +/ ", $pfctlstats[7]);
1589
	$pf_out6_pass = preg_split("/ +/", $pfctlstats[9]);
1590
	$in4_pass = $pf_in4_pass[5];
1591
	$out4_pass = $pf_out4_pass[5];
1592
	$in4_pass_packets = $pf_in4_pass[3];
1593
	$out4_pass_packets = $pf_out4_pass[3];
1594
	$in6_pass = $pf_in6_pass[5];
1595
	$out6_pass = $pf_out6_pass[5];
1596
	$in6_pass_packets = $pf_in6_pass[3];
1597
	$out6_pass_packets = $pf_out6_pass[3];
1598
	$ifinfo['inbytespass'] = $in4_pass + $in6_pass;
1599
	$ifinfo['outbytespass'] = $out4_pass + $out6_pass;
1600
	$ifinfo['inpktspass'] = $in4_pass_packets + $in6_pass_packets;
1601
	$ifinfo['outpktspass'] = $out4_pass_packets + $out6_pass_packets;
1602

    
1603
	/* Block */
1604
	$pf_in4_block = preg_split("/ +/", $pfctlstats[4]);
1605
	$pf_out4_block = preg_split("/ +/", $pfctlstats[6]);
1606
	$pf_in6_block = preg_split("/ +/", $pfctlstats[8]);
1607
	$pf_out6_block = preg_split("/ +/", $pfctlstats[10]);
1608
	$in4_block = $pf_in4_block[5];
1609
	$out4_block = $pf_out4_block[5];
1610
	$in4_block_packets = $pf_in4_block[3];
1611
	$out4_block_packets = $pf_out4_block[3];
1612
	$in6_block = $pf_in6_block[5];
1613
	$out6_block = $pf_out6_block[5];
1614
	$in6_block_packets = $pf_in6_block[3];
1615
	$out6_block_packets = $pf_out6_block[3];
1616
	$ifinfo['inbytesblock'] = $in4_block + $in6_block;
1617
	$ifinfo['outbytesblock'] = $out4_block + $out6_block;
1618
	$ifinfo['inpktsblock'] = $in4_block_packets + $in6_block_packets;
1619
	$ifinfo['outpktsblock'] = $out4_block_packets + $out6_block_packets;
1620

    
1621
	$ifinfo['inbytes'] = $in4_pass + $in6_pass;
1622
	$ifinfo['outbytes'] = $out4_pass + $out6_pass;
1623
	$ifinfo['inpkts'] = $in4_pass_packets + $in6_pass_packets;
1624
	$ifinfo['outpkts'] = $out4_pass_packets + $out6_pass_packets;
1625

    
1626
	$link_type = config_get_path("interfaces/{$ifdescr}/ipaddr");
1627
	switch ($link_type) {
1628
		/* DHCP? -> see if dhclient is up */
1629
		case "dhcp":
1630
			/* see if dhclient is up */
1631
			if (find_dhclient_process($ifinfo['if']) != 0) {
1632
				$ifinfo['dhcplink'] = "up";
1633
			} else {
1634
				$ifinfo['dhcplink'] = "down";
1635
			}
1636

    
1637
			break;
1638
		/* PPPoE/PPTP/L2TP interface? -> get status from virtual interface */
1639
		case "pppoe":
1640
		case "pptp":
1641
		case "l2tp":
1642
			if ($ifinfo['status'] == "up" && !isset($link0)) {
1643
				/* get PPPoE link status for dial on demand */
1644
				$ifinfo["{$link_type}link"] = "up";
1645
			} else {
1646
				$ifinfo["{$link_type}link"] = "down";
1647
			}
1648

    
1649
			break;
1650
		/* PPP interface? -> get uptime for this session and cumulative uptime from the persistent log file in conf */
1651
		case "ppp":
1652
			if ($ifinfo['status'] == "up") {
1653
				$ifinfo['ppplink'] = "up";
1654
			} else {
1655
				$ifinfo['ppplink'] = "down" ;
1656
			}
1657

    
1658
			if (empty($ifinfo['status'])) {
1659
				$ifinfo['status'] = "down";
1660
			}
1661

    
1662
			foreach (config_get_path('ppps/ppp', []) as $ppp) {
1663
				if (config_get_path("interfaces/{$ifdescr}/if") == $ppp['if']) {
1664
					break;
1665
				}
1666
			}
1667
			$dev = $ppp['ports'];
1668
			if (config_get_path("interfaces/{$ifdescr}/if") != $ppp['if'] || empty($dev)) {
1669
				break;
1670
			}
1671
			if (!file_exists($dev)) {
1672
				$ifinfo['nodevice'] = 1;
1673
				$ifinfo['pppinfo'] = $dev . " " . gettext("device not present! Is the modem attached to the system?");
1674
			}
1675

    
1676
			$usbmodemoutput = array();
1677
			exec("/usr/sbin/usbconfig", $usbmodemoutput);
1678
			$mondev = "{$g['tmp_path']}/3gstats.{$ifdescr}";
1679
			if (file_exists($mondev)) {
1680
				$cellstats = file($mondev);
1681
				/* skip header */
1682
				$a_cellstats = explode(",", $cellstats[1]);
1683
				if (preg_match("/huawei/i", implode("\n", $usbmodemoutput))) {
1684
					$ifinfo['cell_rssi'] = huawei_rssi_to_string($a_cellstats[1]);
1685
					$ifinfo['cell_mode'] = huawei_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1686
					$ifinfo['cell_simstate'] = huawei_simstate_to_string($a_cellstats[10]);
1687
					$ifinfo['cell_service'] = huawei_service_to_string(trim($a_cellstats[11]));
1688
				}
1689
				if (preg_match("/zte/i", implode("\n", $usbmodemoutput))) {
1690
					$ifinfo['cell_rssi'] = zte_rssi_to_string($a_cellstats[1]);
1691
					$ifinfo['cell_mode'] = zte_mode_to_string($a_cellstats[2], $a_cellstats[3]);
1692
					$ifinfo['cell_simstate'] = zte_simstate_to_string($a_cellstats[10]);
1693
					$ifinfo['cell_service'] = zte_service_to_string(trim($a_cellstats[11]));
1694
				}
1695
				$ifinfo['cell_upstream'] = $a_cellstats[4];
1696
				$ifinfo['cell_downstream'] = trim($a_cellstats[5]);
1697
				$ifinfo['cell_sent'] = $a_cellstats[6];
1698
				$ifinfo['cell_received'] = trim($a_cellstats[7]);
1699
				$ifinfo['cell_bwupstream'] = $a_cellstats[8];
1700
				$ifinfo['cell_bwdownstream'] = trim($a_cellstats[9]);
1701
			}
1702
			// Calculate cumulative uptime for PPP link. Useful for connections that have per minute/hour contracts so you don't go over!
1703
			if (isset($ppp['uptime'])) {
1704
				$ifinfo['ppp_uptime_accumulated'] = "(".get_ppp_uptime($ifinfo['if']).")";
1705
			}
1706
			break;
1707
		default:
1708
			break;
1709
	}
1710

    
1711
	if (file_exists("{$g['varrun_path']}/{$link_type}_{$ifdescr}.pid")) {
1712
		$sec = trim(`/usr/local/sbin/ppp-uptime.sh {$ifinfo['if']}`);
1713
		$ifinfo['ppp_uptime'] = convert_seconds_to_dhms($sec);
1714
	}
1715

    
1716
	if ($ifinfo['status'] == "up") {
1717
		/* try to determine media with ifconfig */
1718
		$ifconfiginfo = [];
1719
		exec("/sbin/ifconfig -v " . $ifinfo['if'], $ifconfiginfo);
1720
		$wifconfiginfo = [];
1721
		if (is_interface_wireless($ifdescr)) {
1722
			exec("/sbin/ifconfig {$ifinfo['if']} list sta", $wifconfiginfo);
1723
			array_shift($wifconfiginfo);
1724
		}
1725
		$matches = "";
1726
		foreach ($ifconfiginfo as $ici) {
1727

    
1728
			/* don't list media/speed for wireless cards, as it always
1729
			   displays 2 Mbps even though clients can connect at 11 Mbps */
1730
			if (preg_match("/media: .*? \((.*?)\)/", $ici, $matches)) {
1731
				$ifinfo['media'] = $matches[1];
1732
			} else if (preg_match("/media: Ethernet (.*)/", $ici, $matches)) {
1733
				$ifinfo['media'] = $matches[1];
1734
			} else if (preg_match("/media: IEEE 802.11 Wireless Ethernet (.*)/", $ici, $matches)) {
1735
				$ifinfo['media'] = $matches[1];
1736
			}
1737

    
1738
			if (preg_match("/status: (.*)$/", $ici, $matches)) {
1739
				if ($matches[1] != "active") {
1740
					$ifinfo['status'] = $matches[1];
1741
				}
1742
				if ($ifinfo['status'] == gettext("running")) {
1743
					$ifinfo['status'] = gettext("up");
1744
				}
1745
			}
1746
			if (preg_match("/channel (\S*)/", $ici, $matches)) {
1747
				$ifinfo['channel'] = $matches[1];
1748
			}
1749
			if (preg_match("/ssid (\".*?\"|\S*)/", $ici, $matches)) {
1750
				if ($matches[1][0] == '"') {
1751
					$ifinfo['ssid'] = substr($matches[1], 1, -1);
1752
				}
1753
				else {
1754
					$ifinfo['ssid'] = $matches[1];
1755
				}
1756
			}
1757
			if (preg_match("/laggproto (.*)$/", $ici, $matches)) {
1758
				$ifinfo['laggproto'] = $matches[1];
1759
			}
1760
			if (preg_match("/laggport: (.*)$/", $ici, $matches)) {
1761
				$ifinfo['laggport'][] = $matches[1];
1762
			}
1763
			if (preg_match("/plugged: (.*)$/", $ici, $matches)) {
1764
				$ifinfo['plugged'] = $matches[1];
1765
			}
1766
			if (preg_match("/vendor: (.*)$/", $ici, $matches)) {
1767
				$ifinfo['vendor'] = $matches[1];
1768
			}
1769
			if (preg_match("/module temperature: (.*) Voltage: (.*)$/", $ici, $matches)) {
1770
				$ifinfo['temperature'] = $matches[1];
1771
				$ifinfo['voltage'] = $matches[2];
1772
			}
1773
			if (preg_match("/RX: (.*) TX: (.*)$/", $ici, $matches)) {
1774
				$ifinfo['rx'] = $matches[1];
1775
				$ifinfo['tx'] = $matches[2];
1776
			}
1777
		}
1778
		foreach ($wifconfiginfo as $ici) {
1779
			$elements = preg_split("/[ ]+/i", $ici);
1780
			if ($elements[0] != "") {
1781
				$ifinfo['bssid'] = $elements[0];
1782
			}
1783
			if ($elements[3] != "") {
1784
				$ifinfo['rate'] = $elements[3];
1785
			}
1786
			if ($elements[4] != "") {
1787
				$ifinfo['rssi'] = $elements[4];
1788
			}
1789
		}
1790
		/* lookup the gateway */
1791
		if (interface_has_gateway($ifdescr)) {
1792
			$ifinfo['gateway'] = get_interface_gateway($ifdescr);
1793
		}
1794
		if (interface_has_gatewayv6($ifdescr)) {
1795
			$ifinfo['gatewayv6'] = get_interface_gateway_v6($ifdescr);
1796
		}
1797
	}
1798

    
1799
	$bridge = "";
1800
	$bridge = link_interface_to_bridge($ifdescr);
1801
	if ($bridge) {
1802
		$bridge_text = `/sbin/ifconfig {$bridge}`;
1803
		if (stristr($bridge_text, "blocking") <> false) {
1804
			$ifinfo['bridge'] = "<b><font color='red'>" . gettext("blocking") . "</font></b> - " . gettext("check for ethernet loops");
1805
			$ifinfo['bridgeint'] = $bridge;
1806
		} else if (stristr($bridge_text, "learning") <> false) {
1807
			$ifinfo['bridge'] = gettext("learning");
1808
			$ifinfo['bridgeint'] = $bridge;
1809
		} else if (stristr($bridge_text, "forwarding") <> false) {
1810
			$ifinfo['bridge'] = gettext("forwarding");
1811
			$ifinfo['bridgeint'] = $bridge;
1812
		}
1813
	}
1814

    
1815
	return $ifinfo;
1816
}
1817

    
1818
//returns cpu speed of processor. Good for determining capabilities of machine
1819
function get_cpu_speed() {
1820
	return get_single_sysctl("hw.clockrate");
1821
}
1822

    
1823
function get_uptime_sec() {
1824
	$boottime = "";
1825
	$matches = "";
1826
	$boottime = get_single_sysctl("kern.boottime");
1827
	preg_match("/sec = (\d+)/", $boottime, $matches);
1828
	$boottime = $matches[1];
1829
	if (intval($boottime) == 0) {
1830
		return 0;
1831
	}
1832

    
1833
	$uptime = time() - $boottime;
1834
	return $uptime;
1835
}
1836

    
1837
function resolve_host_addresses($host, $recordtypes = array(DNS_A, DNS_AAAA, DNS_CNAME), $index = true) {
1838
	$dnsresult = array();
1839
	$resolved = array();
1840
	$errreporting = error_reporting();
1841
	error_reporting($errreporting & ~E_WARNING);// dns_get_record throws a warning if nothing is resolved..
1842
	foreach ($recordtypes as $recordtype) {
1843
		$tmp = dns_get_record($host, $recordtype);
1844
		if (is_array($tmp)) {
1845
			$dnsresult = array_merge($dnsresult, $tmp);
1846
		}
1847
	}
1848
	error_reporting($errreporting);// restore original php warning/error settings.
1849
	foreach ($dnsresult as $item) {
1850
		$newitem = array();
1851
		$newitem['type'] = $item['type'];
1852
		switch ($item['type']) {
1853
			case 'CNAME':
1854
				$newitem['data'] = $item['target'];
1855
				$resolved[] = $newitem;
1856
				break;
1857
			case 'A':
1858
				$newitem['data'] = $item['ip'];
1859
				$resolved[] = $newitem;
1860
				break;
1861
			case 'AAAA':
1862
				$newitem['data'] = $item['ipv6'];
1863
				$resolved[] = $newitem;
1864
				break;
1865
		}
1866
	}
1867
	if ($index == false) {
1868
		foreach ($resolved as $res) {
1869
			$noind[] = $res['data'];
1870
		}
1871
		$resolved = $noind;
1872
	}
1873
	return $resolved;
1874
}
1875

    
1876
function add_hostname_to_watch($hostname) {
1877
	if (!is_dir("/var/db/dnscache")) {
1878
		mkdir("/var/db/dnscache");
1879
	}
1880
	$result = array();
1881
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1882
		$contents = "";
1883
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1884
		if (!empty($domips)) {
1885
			foreach ($domips as $ip) {
1886
				$contents .= "$ip\n";
1887
			}
1888
		}
1889
		file_put_contents("/var/db/dnscache/$hostname", $contents);
1890
		/* Remove empty elements */
1891
		$result = array_filter(explode("\n", $contents), 'strlen');
1892
	}
1893
	return $result;
1894
}
1895

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

    
1900
function pfsense_default_state_size() {
1901
	/* get system memory amount */
1902
	$memory = get_memory();
1903
	$physmem = $memory[0];
1904
	/* Be cautious and only allocate 10% of system memory to the state table */
1905
	$max_states = (int) ($physmem/10)*1000;
1906
	return $max_states;
1907
}
1908

    
1909
function pfsense_default_tables_size() {
1910
	$current = `pfctl -sm | grep ^tables | awk '{print $4};'`;
1911
	return $current;
1912
}
1913

    
1914
function pfsense_default_table_entries_size() {
1915
	$current = `pfctl -sm | grep table-entries | awk '{print $4};'`;
1916
	return (trim($current));
1917
}
1918

    
1919
/* Compare the current hostname DNS to the DNS cache we made
1920
 * if it has changed we return the old records
1921
 * if no change we return false */
1922
function compare_hostname_to_dnscache($hostname) {
1923
	global $g;
1924
	if (!is_dir("/var/db/dnscache")) {
1925
		mkdir("/var/db/dnscache");
1926
	}
1927
	$hostname = trim($hostname);
1928
	if (is_readable("/var/db/dnscache/{$hostname}")) {
1929
		$oldcontents = file_get_contents("/var/db/dnscache/{$hostname}");
1930
	} else {
1931
		$oldcontents = "";
1932
	}
1933
	if ((is_fqdn($hostname)) && (!is_ipaddr($hostname))) {
1934
		$contents = "";
1935
		$domips = resolve_host_addresses($hostname, array(DNS_A), false);
1936
		if (!empty($domips)) {
1937
			foreach ($domips as $ip) {
1938
				$contents .= "$ip\n";
1939
			}
1940
		}
1941
	}
1942

    
1943
	if (trim($oldcontents) != trim($contents)) {
1944
		if (g_get('debug')) {
1945
			log_error(sprintf(gettext('DNSCACHE: Found old IP %1$s and new IP %2$s'), $oldcontents, $contents));
1946
		}
1947
		return ($oldcontents);
1948
	} else {
1949
		return false;
1950
	}
1951
}
1952

    
1953
/*
1954
 * load_crypto() - Load crypto modules if enabled in config.
1955
 */
1956
function load_crypto() {
1957
	$crypto_modules = array('aesni', 'cryptodev');
1958

    
1959
	$enabled_modules = explode('_', config_get_path('system/crypto_hardware'));
1960

    
1961
	foreach ($enabled_modules as $enmod) {
1962
		if (empty($enmod) || !in_array($enmod, $crypto_modules)) {
1963
			continue;
1964
		}
1965
		if (!is_module_loaded($enmod)) {
1966
			log_error(sprintf(gettext("Loading %s cryptographic accelerator module."), $enmod));
1967
			mute_kernel_msgs();
1968
			mwexec("/sbin/kldload " . escapeshellarg($enmod));
1969
			unmute_kernel_msgs();
1970
		}
1971
	}
1972
}
1973

    
1974
/*
1975
 * load_thermal_hardware() - Load temperature monitor kernel module
1976
 */
1977
function load_thermal_hardware() {
1978
	$thermal_hardware_modules = array('coretemp', 'amdtemp');
1979
	$thermal_hardware = config_get_path('system/thermal_hardware');
1980

    
1981
	if (!in_array($thermal_hardware, $thermal_hardware_modules)) {
1982
		return false;
1983
	}
1984

    
1985
	if (!empty($thermal_hardware) && !is_module_loaded($thermal_hardware)) {
1986
		log_error(sprintf(gettext("Loading %s thermal monitor module."), $thermal_hardware));
1987
		mute_kernel_msgs();
1988
		mwexec("/sbin/kldload {$thermal_hardware}");
1989
		unmute_kernel_msgs();
1990
	}
1991
}
1992

    
1993
/****f* pfsense-utils/isvm
1994
 * NAME
1995
 *   isvm
1996
 * INPUTS
1997
 *	none
1998
 * RESULT
1999
 *   returns true if machine is running under a virtual environment
2000
 ******/
2001
function isvm() {
2002
	$virtualenvs = array("vmware", "parallels", "qemu", "bochs", "plex86", "VirtualBox");
2003
	exec('/bin/kenv -q smbios.system.product 2>/dev/null', $output, $rc);
2004

    
2005
	if ($rc != 0 || !isset($output[0])) {
2006
		return false;
2007
	}
2008

    
2009
	foreach ($virtualenvs as $virtualenv) {
2010
		if (stripos($output[0], $virtualenv) !== false) {
2011
			return true;
2012
		}
2013
	}
2014

    
2015
	return false;
2016
}
2017

    
2018
function get_freebsd_version() {
2019
	$version = explode(".", php_uname("r"));
2020
	return $version[0];
2021
}
2022

    
2023
function download_file($url, $destination, $verify_ssl = true, $connect_timeout = 5, $timeout = 0) {
2024
	global $g;
2025

    
2026
	$fp = fopen($destination, "wb");
2027

    
2028
	if (!$fp) {
2029
		return false;
2030
	}
2031

    
2032
	$ch = curl_init();
2033
	curl_setopt($ch, CURLOPT_URL, $url);
2034
	if ($verify_ssl) {
2035
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2036
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2037
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2038
	} else {
2039
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2040
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2041
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2042
	}
2043
	curl_setopt($ch, CURLOPT_FILE, $fp);
2044
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2045
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2046
	curl_setopt($ch, CURLOPT_HEADER, false);
2047
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2048
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2049
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2050
	} else {
2051
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2052
	}
2053

    
2054
	set_curlproxy($ch);
2055

    
2056
	@curl_exec($ch);
2057
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2058
	fclose($fp);
2059
	curl_close($ch);
2060
	if ($http_code == 200) {
2061
		return true;
2062
	} else {
2063
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2064
		unlink_if_exists($destination);
2065
		return false;
2066
	}
2067
}
2068

    
2069
function download_file_with_progress_bar($url, $destination, $verify_ssl = true, $readbody = 'read_body', $connect_timeout = 5, $timeout = 0) {
2070
	global $g, $ch, $fout, $file_size, $downloaded, $first_progress_update;
2071
	$file_size = 1;
2072
	$downloaded = 1;
2073
	$first_progress_update = TRUE;
2074
	/* open destination file */
2075
	$fout = fopen($destination, "wb");
2076

    
2077
	if (!$fout) {
2078
		return false;
2079
	}
2080
	/*
2081
	 *      Originally by Author: Keyvan Minoukadeh
2082
	 *      Modified by Scott Ullrich to return Content-Length size
2083
	 */
2084
	$ch = curl_init();
2085
	curl_setopt($ch, CURLOPT_URL, $url);
2086
	if ($verify_ssl) {
2087
		curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
2088
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
2089
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
2090
	} else {
2091
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
2092
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2093
		curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false);
2094
	}
2095
	curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
2096
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2097
	curl_setopt($ch, CURLOPT_WRITEFUNCTION, $readbody);
2098
	curl_setopt($ch, CURLOPT_NOPROGRESS, '1');
2099
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
2100
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2101
	if (!config_path_enabled('system','do_not_send_uniqueid')) {
2102
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version') . ':' . system_get_uniqueid());
2103
	} else {
2104
		curl_setopt($ch, CURLOPT_USERAGENT, g_get('product_label') . '/' . g_get('product_version'));
2105
	}
2106

    
2107
	set_curlproxy($ch);
2108

    
2109
	@curl_exec($ch);
2110
	$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2111
	fclose($fout);
2112
	curl_close($ch);
2113
	if ($http_code == 200) {
2114
		return true;
2115
	} else {
2116
		log_error(sprintf(gettext('Download file failed with status code %1$s. URL: %2$s'), $http_code, $url));
2117
		unlink_if_exists($destination);
2118
		return false;
2119
	}
2120
}
2121

    
2122
function read_header($ch, $string) {
2123
	global $file_size;
2124
	$length = strlen($string);
2125
	$regs = "";
2126
	preg_match("/(Content-Length:) (.*)/", $string, $regs);
2127
	if ($regs[2] <> "") {
2128
		$file_size = intval($regs[2]);
2129
	}
2130
	ob_flush();
2131
	return $length;
2132
}
2133

    
2134
function read_body($ch, $string) {
2135
	global $fout, $file_size, $downloaded, $sendto, $static_status, $static_output, $lastseen, $first_progress_update;
2136
	global $pkg_interface;
2137
	$length = strlen($string);
2138
	$downloaded += intval($length);
2139
	if ($file_size > 0) {
2140
		$downloadProgress = round(100 * (1 - $downloaded / $file_size), 0);
2141
		$downloadProgress = 100 - $downloadProgress;
2142
	} else {
2143
		$downloadProgress = 0;
2144
	}
2145
	if ($lastseen <> $downloadProgress and $downloadProgress < 101) {
2146
		if ($sendto == "status") {
2147
			if ($pkg_interface == "console") {
2148
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2149
					$tostatus = $static_status . $downloadProgress . "%";
2150
					if ($downloadProgress == 100) {
2151
						$tostatus = $tostatus . "\r";
2152
					}
2153
					update_status($tostatus);
2154
				}
2155
			} else {
2156
				$tostatus = $static_status . $downloadProgress . "%";
2157
				update_status($tostatus);
2158
			}
2159
		} else {
2160
			if ($pkg_interface == "console") {
2161
				if (($downloadProgress % 10) == 0 || $downloadProgress < 10) {
2162
					$tooutput = $static_output . $downloadProgress . "%";
2163
					if ($downloadProgress == 100) {
2164
						$tooutput = $tooutput . "\r";
2165
					}
2166
					update_output_window($tooutput);
2167
				}
2168
			} else {
2169
				$tooutput = $static_output . $downloadProgress . "%";
2170
				update_output_window($tooutput);
2171
			}
2172
		}
2173
		if (($pkg_interface != "console") || (($downloadProgress % 10) == 0) || ($downloadProgress < 10)) {
2174
			update_progress_bar($downloadProgress, $first_progress_update);
2175
			$first_progress_update = FALSE;
2176
		}
2177
		$lastseen = $downloadProgress;
2178
	}
2179
	if ($fout) {
2180
		fwrite($fout, $string);
2181
	}
2182
	ob_flush();
2183
	return $length;
2184
}
2185

    
2186
/*
2187
 *   update_output_window: update bottom textarea dynamically.
2188
 */
2189
function update_output_window($text) {
2190
	global $pkg_interface;
2191
	$log = preg_replace("/\n/", "\\n", $text);
2192
	if ($pkg_interface != "console") {
2193
?>
2194
<script type="text/javascript">
2195
//<![CDATA[
2196
	document.getElementById("output").textContent="<?=htmlspecialchars($log)?>";
2197
	document.getElementById("output").scrollTop = document.getElementById("output").scrollHeight;
2198
//]]>
2199
</script>
2200
<?php
2201
	}
2202
	/* ensure that contents are written out */
2203
	ob_flush();
2204
}
2205

    
2206
/*
2207
 *   update_status: update top textarea dynamically.
2208
 */
2209
function update_status($status) {
2210
	global $pkg_interface;
2211

    
2212
	if ($pkg_interface == "console") {
2213
		print ("{$status}");
2214
	}
2215

    
2216
	/* ensure that contents are written out */
2217
	ob_flush();
2218
}
2219

    
2220
/*
2221
 * update_progress_bar($percent, $first_time): updates the javascript driven progress bar.
2222
 */
2223
function update_progress_bar($percent, $first_time) {
2224
	global $pkg_interface;
2225
	if ($percent > 100) {
2226
		$percent = 1;
2227
	}
2228
	if ($pkg_interface <> "console") {
2229
		echo '<script type="text/javascript">';
2230
		echo "\n//<![CDATA[\n";
2231
		echo 'document.getElementById("progressbar").style.width="'. $percent.'%"';
2232
		echo "\n//]]>\n";
2233
		echo '</script>';
2234
	} else {
2235
		if (!($first_time)) {
2236
			echo "\x08\x08\x08\x08\x08";
2237
		}
2238
		echo sprintf("%4d%%", $percent);
2239
	}
2240
}
2241

    
2242
function update_alias_name($new_alias_name, $orig_alias_name) {
2243
	if (!$orig_alias_name) {
2244
		return;
2245
	}
2246

    
2247
	// Firewall rules
2248
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2249
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2250
	update_alias_names_upon_change(array('filter', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2251
	update_alias_names_upon_change(array('filter', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2252
	// NAT Rules
2253
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2254
	update_alias_names_upon_change(array('nat', 'rule'), array('source', 'port'), $new_alias_name, $orig_alias_name);
2255
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2256
	update_alias_names_upon_change(array('nat', 'rule'), array('destination', 'port'), $new_alias_name, $orig_alias_name);
2257
	update_alias_names_upon_change(array('nat', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2258
	update_alias_names_upon_change(array('nat', 'rule'), array('local-port'), $new_alias_name, $orig_alias_name);
2259
	// NAT 1:1 Rules
2260
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('external'), $new_alias_name, $orig_alias_name);
2261
	//update_alias_names_upon_change(array('nat', 'onetoone'), array('source', 'address'), $new_alias_name, $orig_alias_name);
2262
	update_alias_names_upon_change(array('nat', 'onetoone'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2263
	// NAT Outbound Rules
2264
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('source', 'network'), $new_alias_name, $orig_alias_name);
2265
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('sourceport'), $new_alias_name, $orig_alias_name);
2266
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('destination', 'address'), $new_alias_name, $orig_alias_name);
2267
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('dstport'), $new_alias_name, $orig_alias_name);
2268
	update_alias_names_upon_change(array('nat', 'outbound', 'rule'), array('target'), $new_alias_name, $orig_alias_name);
2269
	// Alias in an alias
2270
	update_alias_names_upon_change(array('aliases', 'alias'), array('address'), $new_alias_name, $orig_alias_name);
2271
	// Static routes
2272
	update_alias_names_upon_change(array('staticroutes', 'route'), array('network'), $new_alias_name, $orig_alias_name);
2273
	// OpenVPN
2274
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2275
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2276
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_network'), $new_alias_name, $orig_alias_name);
2277
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2278
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_network'), $new_alias_name, $orig_alias_name);
2279
	update_alias_names_upon_change(array('openvpn', 'openvpn-server'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2280
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2281
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2282
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_network'), $new_alias_name, $orig_alias_name);
2283
	update_alias_names_upon_change(array('openvpn', 'openvpn-client'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2284
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_network'), $new_alias_name, $orig_alias_name);
2285
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('tunnel_networkv6'), $new_alias_name, $orig_alias_name);
2286
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_network'), $new_alias_name, $orig_alias_name);
2287
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('local_networkv6'), $new_alias_name, $orig_alias_name);
2288
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_network'), $new_alias_name, $orig_alias_name);
2289
	update_alias_names_upon_change(array('openvpn', 'openvpn-csc'), array('remote_networkv6'), $new_alias_name, $orig_alias_name);
2290
}
2291

    
2292
function update_alias_names_upon_change($section, $field, $new_alias_name, $origname) {
2293
	global $g, $config, $pconfig, $debug;
2294
	if (!$origname) {
2295
		return;
2296
	}
2297

    
2298
	$sectionref = &$config;
2299
	foreach ($section as $sectionname) {
2300
		if (is_array($sectionref) && isset($sectionref[$sectionname])) {
2301
			$sectionref = &$sectionref[$sectionname];
2302
		} else {
2303
			return;
2304
		}
2305
	}
2306

    
2307
	if ($debug) {
2308
		$fd = fopen("{$g['tmp_path']}/print_r", "a");
2309
		fwrite($fd, print_r($pconfig, true));
2310
	}
2311

    
2312
	if (is_array($sectionref)) {
2313
		foreach (array_keys($sectionref) as $itemkey) {
2314
			if ($debug) {
2315
				fwrite($fd, "$itemkey\n");
2316
			}
2317

    
2318
			$fieldfound = true;
2319
			$fieldref = &$sectionref[$itemkey];
2320
			foreach ($field as $fieldname) {
2321
				if (is_array($fieldref) && isset($fieldref[$fieldname])) {
2322
					$fieldref = &$fieldref[$fieldname];
2323
				} else {
2324
					$fieldfound = false;
2325
					break;
2326
				}
2327
			}
2328
			if ($fieldfound && $fieldref == $origname) {
2329
				if ($debug) {
2330
					fwrite($fd, "Setting old alias value $origname to $new_alias_name\n");
2331
				}
2332
				$fieldref = $new_alias_name;
2333
			}
2334
		}
2335
	}
2336

    
2337
	if ($debug) {
2338
		fclose($fd);
2339
	}
2340

    
2341
}
2342

    
2343
function parse_aliases_file($filename, $type = "url", $max_items = -1, $kflc = false) {
2344
	/*
2345
	 * $filename = file to process for example blocklist like DROP:  http://www.spamhaus.org/drop/drop.txt
2346
	 * $type = if set to 'url' then subnets and ips will be returned,
2347
	 *         if set to 'url_ports' port-ranges and ports will be returned
2348
	 * $max_items = sets the maximum amount of valid items to load, -1 the default defines there is no limit.
2349
	 *
2350
	 * RETURNS an array of ip subnets and ip's or ports and port-ranges, returns NULL upon a error conditions (file not found)
2351
	 */
2352

    
2353
	if (!file_exists($filename)) {
2354
		log_error(sprintf(gettext("Could not process non-existent file from alias: %s"), $filename));
2355
		return null;
2356
	}
2357

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

    
2423
function update_alias_url_data() {
2424
	global $g, $aliastable;
2425

    
2426
	$updated = false;
2427

    
2428
	/* item is a url type */
2429
	$lockkey = lock('aliasurl');
2430
	$aliases = array();
2431
	$aliases_nested = array();
2432

    
2433
	foreach (config_get_path('aliases/alias', []) as $x => $alias) {
2434
		if (empty($alias['aliasurl'])) {
2435
			continue;
2436
		}
2437
		foreach ($alias['aliasurl'] as $alias_url) {
2438
			if (is_alias($alias_url)) {
2439
				// process nested URL aliases after URL-only aliases
2440
				$aliases_nested[] = $x;
2441
				continue 2;
2442
			}
2443
		}
2444
		$aliases[] = $x;
2445
	}
2446

    
2447
	foreach (array_merge($aliases, $aliases_nested) as $x) {
2448

    
2449
		$address = array();
2450
		$type = config_get_path("aliases/alias/{$x}/type");
2451
		foreach (config_get_path("aliases/alias/{$x}/aliasurl") as $alias_url) {
2452
			/* fetch down and add in */
2453
			if (is_URL($alias_url)) {
2454
				$temp_filename = tempnam("{$g['tmp_path']}/", "alias_import");
2455
				rmdir_recursive($temp_filename);
2456
				$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2457
				mkdir($temp_filename);
2458
				if (!download_file($alias_url, $temp_filename . "/aliases", $verify_ssl)) {
2459
					log_error(sprintf(gettext("Failed to download alias %s"), $alias_url));
2460
					rmdir_recursive($temp_filename);
2461
					continue;
2462
				}
2463

    
2464
				/* if the item is tar gzipped then extract */
2465
				if (stripos($alias_url, '.tgz') && !process_alias_tgz($temp_filename)) {
2466
					log_error(sprintf(gettext("Could not unpack tgz from the URL '%s'."), $alias_url));
2467
					rmdir_recursive($temp_filename);
2468
					continue;
2469
				}
2470
				if (file_exists("{$temp_filename}/aliases")) {
2471
					$t_address = parse_aliases_file("{$temp_filename}/aliases", $type, 5000);
2472
					if ($t_address == null) {
2473
						/* nothing was found */
2474
						log_error(sprintf(gettext("Could not fetch usable data from '%s'."), $alias_url));
2475
						//rmdir_recursive($temp_filename);
2476
						continue;
2477
					} else {
2478
						array_push($address, ...$t_address);
2479
					}
2480
					unset($t_address);
2481
				}
2482
				rmdir_recursive($temp_filename);
2483
			} elseif (is_alias($alias_url)) {
2484
				/* nested URL alias, see https://redmine.pfsense.org/issues/11863 */
2485
				if (!$aliastable) {
2486
					alias_make_table();
2487
				}
2488
				$t_address = explode(" ", $aliastable[$alias_url]);
2489
				if ($t_address == null) {
2490
					log_error(sprintf(gettext("Could not get usable data from '%s' URL alias."), $alias_url));
2491
					continue;
2492
				}
2493
				array_push($address, ...$t_address);
2494
			}
2495
			if (!empty($address)) {
2496
				config_set_path("aliases/alias/{$x}/address", implode(" ", $address));
2497
				$updated = true;
2498
			}
2499
		}
2500
	}
2501

    
2502
	unlock($lockkey);
2503

    
2504
	/* Report status to callers as well */
2505
	return $updated;
2506
}
2507

    
2508
function process_alias_tgz($temp_filename) {
2509
	if (!file_exists('/usr/bin/tar')) {
2510
		log_error(gettext("Alias archive is a .tar/tgz file which cannot be decompressed because utility is missing!"));
2511
		return false;
2512
	}
2513
	rename("{$temp_filename}/aliases", "{$temp_filename}/aliases.tgz");
2514
	mwexec("/usr/bin/tar xzf {$temp_filename}/aliases.tgz -C {$temp_filename}/aliases/");
2515
	unlink("{$temp_filename}/aliases.tgz");
2516
	$files_to_process = return_dir_as_array("{$temp_filename}/");
2517
	/* foreach through all extracted files and build up aliases file */
2518
	$fd = @fopen("{$temp_filename}/aliases", "w");
2519
	if (!$fd) {
2520
		log_error(sprintf(gettext("Could not open %s/aliases for writing!"), $temp_filename));
2521
		return false;
2522
	}
2523
	foreach ($files_to_process as $f2p) {
2524
		$tmpfd = @fopen($f2p, 'r');
2525
		if (!$tmpfd) {
2526
			log_error(sprintf(gettext('The following file could not be read %1$s from %2$s'), $f2p, $temp_filename));
2527
			continue;
2528
		}
2529
		while (($tmpbuf = fread($tmpfd, 65536)) !== FALSE) {
2530
			fwrite($fd, $tmpbuf);
2531
		}
2532
		fclose($tmpfd);
2533
		unlink($f2p);
2534
	}
2535
	fclose($fd);
2536
	unset($tmpbuf);
2537

    
2538
	return true;
2539
}
2540

    
2541
function version_compare_dates($a, $b) {
2542
	$a_time = strtotime($a);
2543
	$b_time = strtotime($b);
2544

    
2545
	if ((!$a_time) || (!$b_time)) {
2546
		return FALSE;
2547
	} else {
2548
		if ($a_time < $b_time) {
2549
			return -1;
2550
		} elseif ($a_time == $b_time) {
2551
			return 0;
2552
		} else {
2553
			return 1;
2554
		}
2555
	}
2556
}
2557
function version_get_string_value($a) {
2558
	$strs = array(
2559
		0 => "ALPHA-ALPHA",
2560
		2 => "ALPHA",
2561
		3 => "BETA",
2562
		4 => "B",
2563
		5 => "C",
2564
		6 => "D",
2565
		7 => "RC",
2566
		8 => "RELEASE",
2567
		9 => "*"			// Matches all release levels
2568
	);
2569
	$major = 0;
2570
	$minor = 0;
2571
	foreach ($strs as $num => $str) {
2572
		if (substr($a, 0, strlen($str)) == $str) {
2573
			$major = $num;
2574
			$n = substr($a, strlen($str));
2575
			if (is_numeric($n)) {
2576
				$minor = $n;
2577
			}
2578
			break;
2579
		}
2580
	}
2581
	return "{$major}.{$minor}";
2582
}
2583
function version_compare_string($a, $b) {
2584
	// Only compare string parts if both versions give a specific release
2585
	// (If either version lacks a string part, assume intended to match all release levels)
2586
	if (isset($a) && isset($b)) {
2587
		return version_compare_numeric(version_get_string_value($a), version_get_string_value($b));
2588
	} else {
2589
		return 0;
2590
	}
2591
}
2592
function version_compare_numeric($a, $b) {
2593
	$a_arr = explode('.', rtrim($a, '.'));
2594
	$b_arr = explode('.', rtrim($b, '.'));
2595

    
2596
	foreach ($a_arr as $n => $val) {
2597
		if (array_key_exists($n, $b_arr)) {
2598
			// So far so good, both have values at this minor version level. Compare.
2599
			if ($val > $b_arr[$n]) {
2600
				return 1;
2601
			} elseif ($val < $b_arr[$n]) {
2602
				return -1;
2603
			}
2604
		} else {
2605
			// a is greater, since b doesn't have any minor version here.
2606
			return 1;
2607
		}
2608
	}
2609
	if (count($b_arr) > count($a_arr)) {
2610
		// b is longer than a, so it must be greater.
2611
		return -1;
2612
	} else {
2613
		// Both a and b are of equal length and value.
2614
		return 0;
2615
	}
2616
}
2617
function pfs_version_compare($cur_time, $cur_text, $remote) {
2618
	// First try date compare
2619
	$v = version_compare_dates($cur_time, $remote);
2620
	if ($v === FALSE) {
2621
		// If that fails, try to compare by string
2622
		// Before anything else, simply test if the strings are equal
2623
		if (($cur_text == $remote) || ($cur_time == $remote)) {
2624
			return 0;
2625
		}
2626
		list($cur_num, $cur_str) = explode('-', $cur_text);
2627
		list($rem_num, $rem_str) = explode('-', $remote);
2628

    
2629
		// First try to compare the numeric parts of the version string.
2630
		$v = version_compare_numeric($cur_num, $rem_num);
2631

    
2632
		// If the numeric parts are the same, compare the string parts.
2633
		if ($v == 0) {
2634
			return version_compare_string($cur_str, $rem_str);
2635
		}
2636
	}
2637
	return $v;
2638
}
2639
function process_alias_urltable($name, $type, $url, $freq, $forceupdate=false, $validateonly=false) {
2640
	global $g;
2641

    
2642
	if (!is_validaliasname($name) || !filter_var($url, FILTER_VALIDATE_URL)) {
2643
		return false;
2644
	}
2645

    
2646
	$urltable_prefix = "/var/db/aliastables/";
2647
	$urltable_filename = $urltable_prefix . basename($name) . ".txt";
2648
	$tmp_urltable_filename = $urltable_filename . ".tmp";
2649

    
2650
	// Make the aliases directory if it doesn't exist
2651
	if (!file_exists($urltable_prefix)) {
2652
		mkdir($urltable_prefix);
2653
	} elseif (!is_dir($urltable_prefix)) {
2654
		unlink($urltable_prefix);
2655
		mkdir($urltable_prefix);
2656
	}
2657

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

    
2663
		// Try to fetch the URL supplied
2664
		unlink_if_exists($tmp_urltable_filename);
2665
		$verify_ssl = config_path_enabled('system','checkaliasesurlcert');
2666
		if (download_file($url, $tmp_urltable_filename, $verify_ssl)) {
2667
			// Convert lines that begin with '$' or ';' to comments '#' instead of deleting them.
2668
			mwexec("/usr/bin/sed -i \"\" -E 's/^[[:space:]]*($|#|;)/#/g; /^#/!s/\;.*//g;' ". escapeshellarg($tmp_urltable_filename));
2669

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

    
2672
			$parsed_contents = parse_aliases_file($tmp_urltable_filename, $type, "-1", true);
2673
			if ($type == "urltable_ports") {
2674
				$parsed_contents = group_ports($parsed_contents, true);
2675
			}
2676
			if (is_array($parsed_contents)) {
2677
				file_put_contents($urltable_filename, implode("\n", $parsed_contents));
2678
			} else {
2679
				touch($urltable_filename);
2680
			}
2681

    
2682
			/* Remove existing archive and create an up to date archive if RAM disk is enabled. */
2683
			unlink_if_exists("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz");
2684
			if (config_path_enabled('system','use_mfs_tmpvar') && !file_exists("/conf/ram_disks_failed")) {
2685
				mwexec("/usr/bin/tar -czf " . escapeshellarg("{$g['cf_conf_path']}/RAM_Disk_Store/{$name}.txt.tgz") . " -C / " . escapeshellarg($urltable_filename));
2686
			}
2687

    
2688
			unlink_if_exists($tmp_urltable_filename);
2689
		} else {
2690
			if (!$validateonly) {
2691
				touch($urltable_filename);
2692
			}
2693
			return false;
2694
		}
2695
		return true;
2696
	} else {
2697
		// File exists, and it doesn't need to be updated.
2698
		return -1;
2699
	}
2700
}
2701

    
2702
function get_include_contents($filename) {
2703
	if (is_file($filename)) {
2704
		ob_start();
2705
		include $filename;
2706
		$contents = ob_get_contents();
2707
		ob_end_clean();
2708
		return $contents;
2709
	}
2710
	return false;
2711
}
2712

    
2713
/* This xml 2 array function is courtesy of the php.net comment section on xml_parse.
2714
 * it is roughly 4 times faster then our existing pfSense parser but due to the large
2715
 * size of the RRD xml dumps this is required.
2716
 * The reason we do not use it for pfSense is that it does not know about array fields
2717
 * which causes it to fail on array fields with single items. Possible Todo?
2718
 */
2719
function xml2array($contents, $get_attributes = 1, $priority = 'tag') {
2720
	if (!function_exists('xml_parser_create')) {
2721
		return array ();
2722
	}
2723
	$parser = xml_parser_create('');
2724
	xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
2725
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2726
	xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2727
	xml_parse_into_struct($parser, trim($contents), $xml_values);
2728
	xml_parser_free($parser);
2729
	if (!$xml_values) {
2730
		return; //Hmm...
2731
	}
2732
	$xml_array = array ();
2733
	$parents = array ();
2734
	$opened_tags = array ();
2735
	$arr = array ();
2736
	$current = & $xml_array;
2737
	$repeated_tag_index = array ();
2738
	foreach ($xml_values as $data) {
2739
		unset ($attributes, $value);
2740
		extract($data);
2741
		$result = array ();
2742
		$attributes_data = array ();
2743
		if (isset ($value)) {
2744
			if ($priority == 'tag') {
2745
				$result = $value;
2746
			} else {
2747
				$result['value'] = $value;
2748
			}
2749
		}
2750
		if (isset ($attributes) and $get_attributes) {
2751
			foreach ($attributes as $attr => $val) {
2752
				if ($priority == 'tag') {
2753
					$attributes_data[$attr] = $val;
2754
				} else {
2755
					$result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
2756
				}
2757
			}
2758
		}
2759
		if ($type == "open") {
2760
			$parent[$level -1] = & $current;
2761
			if (!is_array($current) or (!in_array($tag, array_keys($current)))) {
2762
				$current[$tag] = $result;
2763
				if ($attributes_data) {
2764
					$current[$tag . '_attr'] = $attributes_data;
2765
				}
2766
				$repeated_tag_index[$tag . '_' . $level] = 1;
2767
				$current = &$current[$tag];
2768
			} else {
2769
				if (isset ($current[$tag][0])) {
2770
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2771
					$repeated_tag_index[$tag . '_' . $level]++;
2772
				} else {
2773
					$current[$tag] = array (
2774
						$current[$tag],
2775
						$result
2776
						);
2777
					$repeated_tag_index[$tag . '_' . $level] = 2;
2778
					if (isset ($current[$tag . '_attr'])) {
2779
						$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2780
						unset ($current[$tag . '_attr']);
2781
					}
2782
				}
2783
				$last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
2784
				$current = &$current[$tag][$last_item_index];
2785
			}
2786
		} elseif ($type == "complete") {
2787
			if (!isset ($current[$tag])) {
2788
				$current[$tag] = $result;
2789
				$repeated_tag_index[$tag . '_' . $level] = 1;
2790
				if ($priority == 'tag' and $attributes_data) {
2791
					$current[$tag . '_attr'] = $attributes_data;
2792
				}
2793
			} else {
2794
				if (isset ($current[$tag][0]) and is_array($current[$tag])) {
2795
					$current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
2796
					if ($priority == 'tag' and $get_attributes and $attributes_data) {
2797
						$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2798
					}
2799
					$repeated_tag_index[$tag . '_' . $level]++;
2800
				} else {
2801
					$current[$tag] = array (
2802
						$current[$tag],
2803
						$result
2804
						);
2805
					$repeated_tag_index[$tag . '_' . $level] = 1;
2806
					if ($priority == 'tag' and $get_attributes) {
2807
						if (isset ($current[$tag . '_attr'])) {
2808
							$current[$tag]['0_attr'] = $current[$tag . '_attr'];
2809
							unset ($current[$tag . '_attr']);
2810
						}
2811
						if ($attributes_data) {
2812
							$current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
2813
						}
2814
					}
2815
					$repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
2816
				}
2817
			}
2818
		} elseif ($type == 'close') {
2819
			$current = &$parent[$level -1];
2820
		}
2821
	}
2822
	return ($xml_array);
2823
}
2824

    
2825
function get_country_name($country_code = "ALL") {
2826
	if ($country_code != "ALL" && strlen($country_code) != 2) {
2827
		return "";
2828
	}
2829

    
2830
	$country_names_xml = "/usr/local/share/pfSense/iso_3166-1_list_en.xml";
2831
	$country_names_contents = file_get_contents($country_names_xml);
2832
	$country_names = xml2array($country_names_contents);
2833

    
2834
	if ($country_code == "ALL") {
2835
		$country_list = array();
2836
		foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2837
			$country_list[] = array(
2838
				"code" => $country['ISO_3166-1_Alpha-2_Code_element'],
2839
				"name" => ucwords(strtolower($country['ISO_3166-1_Country_name'])));
2840
		}
2841
		return $country_list;
2842
	}
2843

    
2844
	foreach ($country_names['ISO_3166-1_List_en']['ISO_3166-1_Entry'] as $country) {
2845
		if ($country['ISO_3166-1_Alpha-2_Code_element'] == strtoupper($country_code)) {
2846
			return ucwords(strtolower($country['ISO_3166-1_Country_name']));
2847
		}
2848
	}
2849
	return "";
2850
}
2851

    
2852
/* Return the list of country codes to be used on CAs and certs */
2853
function get_cert_country_codes() {
2854
	$countries = get_country_name();
2855

    
2856
	$country_codes = array();
2857
	foreach ($countries as $country) {
2858
		$country_codes[$country['code']] = $country['code'];
2859
	}
2860
	ksort($country_codes);
2861

    
2862
	/* Preserve historical order: None, US, CA, other countries */
2863
	$first_items[''] = gettext("None");
2864
	$first_items['US'] = $country_codes['US'];
2865
	$first_items['CA'] = $country_codes['CA'];
2866
	unset($country_codes['US']);
2867
	unset($country_codes['CA']);
2868

    
2869
	return array_merge($first_items, $country_codes);
2870
}
2871

    
2872
/* sort by interface only, retain the original order of rules that apply to
2873
   the same interface */
2874
function filter_rules_sort() {
2875
	init_config_arr(array('filter', 'rule'));
2876
	$rules = config_get_path('filter/rule', []);
2877

    
2878
	/* mark each rule with the sequence number (to retain the order while sorting) */
2879
	for ($i = 0; isset($rules[$i]); $i++) {
2880
		$rules[$i]['seq'] =$i;
2881
	}
2882
	usort($rules, "filter_rules_compare");
2883

    
2884
	/* strip the sequence numbers again */
2885
	for ($i = 0; isset($rules[$i]); $i++) {
2886
		unset($rules[$i]['seq']);
2887
	}
2888

    
2889
	/* commit changes */
2890
	config_set_path('filter/rule', $rules);
2891
}
2892
function filter_rules_compare($a, $b) {
2893
	if (isset($a['floating']) && isset($b['floating'])) {
2894
		return $a['seq'] - $b['seq'];
2895
	} else if (isset($a['floating'])) {
2896
		return -1;
2897
	} else if (isset($b['floating'])) {
2898
		return 1;
2899
	} else if ($a['interface'] == $b['interface']) {
2900
		return $a['seq'] - $b['seq'];
2901
	} else {
2902
		return compare_interface_friendly_names($a['interface'], $b['interface']);
2903
	}
2904
}
2905

    
2906
function generate_ipv6_from_mac($mac) {
2907
	$elements = explode(":", $mac);
2908
	if (count($elements) <> 6) {
2909
		return false;
2910
	}
2911

    
2912
	$i = 0;
2913
	$ipv6 = "fe80::";
2914
	foreach ($elements as $byte) {
2915
		if ($i == 0) {
2916
			$hexadecimal = substr($byte, 1, 2);
2917
			$bitmap = base_convert($hexadecimal, 16, 2);
2918
			$bitmap = str_pad($bitmap, 4, "0", STR_PAD_LEFT);
2919
			$bitmap = substr($bitmap, 0, 2) ."1". substr($bitmap, 3, 4);
2920
			$byte = substr($byte, 0, 1) . base_convert($bitmap, 2, 16);
2921
		}
2922
		$ipv6 .= $byte;
2923
		if ($i == 1) {
2924
			$ipv6 .= ":";
2925
		}
2926
		if ($i == 3) {
2927
			$ipv6 .= ":";
2928
		}
2929
		if ($i == 2) {
2930
			$ipv6 .= "ff:fe";
2931
		}
2932

    
2933
		$i++;
2934
	}
2935
	return $ipv6;
2936
}
2937

    
2938
/****f* pfsense-utils/load_mac_manufacturer_table
2939
 * NAME
2940
 *   load_mac_manufacturer_table
2941
 * INPUTS
2942
 *   none
2943
 * RESULT
2944
 *   returns associative array with MAC-Manufacturer pairs
2945
 ******/
2946
function load_mac_manufacturer_table() {
2947
	/* load MAC-Manufacture data from the file */
2948
	$macs = false;
2949
	if (file_exists("/usr/local/share/nmap/nmap-mac-prefixes")) {
2950
		$macs=file("/usr/local/share/nmap/nmap-mac-prefixes");
2951
	}
2952
	if ($macs) {
2953
		foreach ($macs as $line) {
2954
			if (preg_match('/([0-9A-Fa-f]{6}) (.*)$/', $line, $matches)) {
2955
				/* store values like this $mac_man['000C29']='VMware' */
2956
				$mac_man["$matches[1]"] = $matches[2];
2957
			}
2958
		}
2959
		return $mac_man;
2960
	} else {
2961
		return -1;
2962
	}
2963

    
2964
}
2965

    
2966
/****f* pfsense-utils/is_ipaddr_configured
2967
 * NAME
2968
 *   is_ipaddr_configured
2969
 * INPUTS
2970
 *   IP Address to check.
2971
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2972
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2973
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2974
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2975
 *     If check_subnets is true and cidrprefix is specified,
2976
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2977
 * RESULT
2978
 *   returns true if the IP Address is configured and present on this device or overlaps a configured subnet.
2979
*/
2980
function is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
2981
	if (count(where_is_ipaddr_configured($ipaddr, $ignore_if, $check_localip, $check_subnets, $cidrprefix))) {
2982
		return true;
2983
	}
2984
	return false;
2985
}
2986

    
2987
/****f* pfsense-utils/where_is_ipaddr_configured
2988
 * NAME
2989
 *   where_is_ipaddr_configured
2990
 * INPUTS
2991
 *   IP Address to check.
2992
 *   If ignore_if is a VIP (not carp), vip array index is passed after string _virtualip
2993
 *   check_localip - if true then also check for matches with PPTP and L2TP addresses
2994
 *   check_subnets - if true then check if the given ipaddr is contained anywhere in the subnet of any other configured IP address
2995
 *   cidrprefix - the CIDR prefix (16, 20, 24, 64...) of ipaddr.
2996
 *     If check_subnets is true and cidrprefix is specified,
2997
 *     then check if the ipaddr/cidrprefix subnet overlaps the subnet of any other configured IP address
2998
 * RESULT
2999
 *   Returns an array of the interfaces 'if' plus IP address or subnet 'ip_or_subnet' that match or overlap the IP address to check.
3000
 *   If there are no matches then an empty array is returned.
3001
*/
3002
function where_is_ipaddr_configured($ipaddr, $ignore_if = "", $check_localip = false, $check_subnets = false, $cidrprefix = "") {
3003
	$where_configured = array();
3004

    
3005
	$pos = strpos($ignore_if, '_virtualip');
3006
	if ($pos !== false) {
3007
		$ignore_vip_id = substr($ignore_if, $pos+10);
3008
		$ignore_vip_if = substr($ignore_if, 0, $pos);
3009
	} else {
3010
		$ignore_vip_id = -1;
3011
		$ignore_vip_if = $ignore_if;
3012
	}
3013

    
3014
	$isipv6 = is_ipaddrv6($ipaddr);
3015

    
3016
	if ($isipv6) {
3017
		$ipaddr = text_to_compressed_ip6($ipaddr);
3018
	}
3019

    
3020
	if ($check_subnets) {
3021
		$cidrprefix = intval($cidrprefix);
3022
		if ($isipv6) {
3023
			if (($cidrprefix < 1) || ($cidrprefix > 128)) {
3024
				$cidrprefix = 128;
3025
			}
3026
		} else {
3027
			if (($cidrprefix < 1) || ($cidrprefix > 32)) {
3028
				$cidrprefix = 32;
3029
			}
3030
		}
3031
		$iflist = get_configured_interface_list();
3032
		foreach ($iflist as $if => $ifname) {
3033
			if ($ignore_if == $if) {
3034
				continue;
3035
			}
3036

    
3037
			if ($isipv6) {
3038
				$if_ipv6 = get_interface_ipv6($if);
3039
				$if_snbitsv6 = get_interface_subnetv6($if);
3040
				/* do not check subnet overlapping on 6rd interfaces,
3041
				 * see https://redmine.pfsense.org/issues/12371 */ 
3042
				if ($if_ipv6 && $if_snbitsv6 &&
3043
				    ((config_get_path("interfaces/{$if}/ipaddrv6") != '6rd') || ($cidrprefix > $if_snbitsv6)) &&
3044
				    check_subnetsv6_overlap($ipaddr, $cidrprefix, $if_ipv6, $if_snbitsv6)) {
3045
					$where_entry = array();
3046
					$where_entry['if'] = $if;
3047
					$where_entry['ip_or_subnet'] = get_interface_ipv6($if) . "/" . get_interface_subnetv6($if);
3048
					$where_configured[] = $where_entry;
3049
				}
3050
			} else {
3051
				$if_ipv4 = get_interface_ip($if);
3052
				$if_snbitsv4 = get_interface_subnet($if);
3053
				if ($if_ipv4 && $if_snbitsv4 && check_subnets_overlap($ipaddr, $cidrprefix, $if_ipv4, $if_snbitsv4)) {
3054
					$where_entry = array();
3055
					$where_entry['if'] = $if;
3056
					$where_entry['ip_or_subnet'] = get_interface_ip($if) . "/" . get_interface_subnet($if);
3057
					$where_configured[] = $where_entry;
3058
				}
3059
			}
3060
		}
3061
	} else {
3062
		if ($isipv6) {
3063
			$interface_list_ips = get_configured_ipv6_addresses();
3064
		} else {
3065
			$interface_list_ips = get_configured_ip_addresses();
3066
		}
3067

    
3068
		foreach ($interface_list_ips as $if => $ilips) {
3069
			if ($ignore_if == $if) {
3070
				continue;
3071
			}
3072
			if (strcasecmp($ipaddr, $ilips) == 0) {
3073
				$where_entry = array();
3074
				$where_entry['if'] = $if;
3075
				$where_entry['ip_or_subnet'] = $ilips;
3076
				$where_configured[] = $where_entry;
3077
			}
3078
		}
3079
	}
3080

    
3081
	if ($check_localip) {
3082
		if (strcasecmp($ipaddr, text_to_compressed_ip6(config_get_path('l2tp/localip', ""))) == 0) {
3083
			$where_entry = array();
3084
			$where_entry['if'] = 'l2tp';
3085
			$where_entry['ip_or_subnet'] = config_get_path('l2tp/localip');
3086
			$where_configured[] = $where_entry;
3087
		}
3088
	}
3089

    
3090
	return $where_configured;
3091
}
3092

    
3093
/****f* pfsense-utils/pfSense_handle_custom_code
3094
 * NAME
3095
 *   pfSense_handle_custom_code
3096
 * INPUTS
3097
 *   directory name to process
3098
 * RESULT
3099
 *   globs the directory and includes the files
3100
 */
3101
function pfSense_handle_custom_code($src_dir) {
3102
	// Allow extending of the nat edit page and include custom input validation
3103
	if (is_dir("$src_dir")) {
3104
		$cf = glob($src_dir . "/*.inc");
3105
		foreach ($cf as $nf) {
3106
			if ($nf == "." || $nf == "..") {
3107
				continue;
3108
			}
3109
			// Include the extra handler
3110
			include_once("$nf");
3111
		}
3112
	}
3113
}
3114

    
3115
function set_language() {
3116
	global $g;
3117

    
3118
	$lang = "";
3119
	if (!empty(config_get_path('system/language'))) {
3120
		$lang = config_get_path('system/language');
3121
	} elseif (!empty(g_get('language'))) {
3122
		$lang = g_get('language');
3123
	}
3124
	$lang .= ".UTF-8";
3125

    
3126
	putenv("LANG={$lang}");
3127
	setlocale(LC_ALL, $lang);
3128
	textdomain("pfSense");
3129
	bindtextdomain("pfSense", "/usr/local/share/locale");
3130
	bind_textdomain_codeset("pfSense", $lang);
3131
}
3132

    
3133
function get_locale_list() {
3134
	$locales = array(
3135
		"bs" => gettext("Bosnian"),
3136
		"zh_CN" => gettext("Chinese"),
3137
		"zh_Hans_CN" => gettext("Chinese (Simplified, China)"),
3138
		"zh_Hans_HK" => gettext("Chinese (Simplified, Hong Kong SAR China)"),
3139
		"zh_Hant_TW" => gettext("Chinese (Traditional, Taiwan)"),
3140
		"nl" => gettext("Dutch"),
3141
		"en_US" => gettext("English"),
3142
		"fr" => gettext("French"),
3143
		"de_DE" => gettext("German (Germany)"),
3144
		"it" => gettext("Italian"),
3145
		"ko" => gettext("Korean"),
3146
		"nb" => gettext("Norwegian Bokmål"),
3147
		"pl" => gettext("Polish"),
3148
		"pt_PT" => gettext("Portuguese"),
3149
		"pt_BR" => gettext("Portuguese (Brazil)"),
3150
		"ru" => gettext("Russian"),
3151
		"es" => gettext("Spanish"),
3152
		"es_AR" => gettext("Spanish (Argentina)"),
3153
	);
3154

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

    
3159
	//asort($locales);
3160

    
3161
	return $locales;
3162
}
3163

    
3164
function return_hex_ipv4($ipv4) {
3165
	if (!is_ipaddrv4($ipv4)) {
3166
		return(false);
3167
	}
3168

    
3169
	/* we need the hex form of the interface IPv4 address */
3170
	$ip4arr = explode(".", $ipv4);
3171
	return (sprintf("%02x%02x%02x%02x", $ip4arr[0], $ip4arr[1], $ip4arr[2], $ip4arr[3]));
3172
}
3173

    
3174
function convert_ipv6_to_128bit($ipv6) {
3175
	if (!is_ipaddrv6($ipv6)) {
3176
		return(false);
3177
	}
3178

    
3179
	$ip6arr = array();
3180
	$ip6prefix = Net_IPv6::uncompress($ipv6);
3181
	$ip6arr = explode(":", $ip6prefix);
3182
	/* binary presentation of the prefix for all 128 bits. */
3183
	$ip6prefixbin = "";
3184
	foreach ($ip6arr as $element) {
3185
		$ip6prefixbin .= sprintf("%016b", hexdec($element));
3186
	}
3187
	return($ip6prefixbin);
3188
}
3189

    
3190
function convert_128bit_to_ipv6($ip6bin) {
3191
	if (strlen($ip6bin) <> 128) {
3192
		return(false);
3193
	}
3194

    
3195
	$ip6arr = array();
3196
	$ip6binarr = array();
3197
	$ip6binarr = str_split($ip6bin, 16);
3198
	foreach ($ip6binarr as $binpart) {
3199
		$ip6arr[] = dechex(bindec($binpart));
3200
	}
3201
	$ip6addr = text_to_compressed_ip6(implode(":", $ip6arr));
3202

    
3203
	return($ip6addr);
3204
}
3205

    
3206

    
3207
/* Returns the calculated bit length of the prefix delegation from the WAN interface */
3208
/* DHCP-PD is variable, calculate from the prefix-len on the WAN interface */
3209
/* 6rd is variable, calculate from 64 - (v6 prefixlen - (32 - v4 prefixlen)) */
3210
/* 6to4 is 16 bits, e.g. 65535 */
3211
function calculate_ipv6_delegation_length($if) {
3212
	$cfg = config_get_path("interfaces/{$if}");
3213
	if (!is_array($cfg)) {
3214
		return false;
3215
	}
3216

    
3217
	switch ($cfg['ipaddrv6']) {
3218
		case "6to4":
3219
			$pdlen = 16;
3220
			break;
3221
		case "6rd":
3222
			$rd6plen = explode("/", $cfg['prefix-6rd']);
3223
			$pdlen = (64 - ((int) $rd6plen[1] + (32 - (int) $cfg['prefix-6rd-v4plen'])));
3224
			break;
3225
		case "dhcp6":
3226
			$pdlen = $cfg['dhcp6-ia-pd-len'];
3227
			break;
3228
		default:
3229
			$pdlen = 0;
3230
			break;
3231
	}
3232
	return($pdlen);
3233
}
3234

    
3235
function merge_ipv6_delegated_prefix($prefix, $suffix, $len = 64) {
3236
	/* convert zero-value prefix IPv6 addresses with IPv4 mapping to hex
3237
	 * see https://redmine.pfsense.org/issues/12440 */
3238
	$suffix_list = explode(':', $suffix);
3239
	if (is_ipaddrv4($suffix_list[count($suffix_list) - 1])) {
3240
		$hexsuffix = dechex(ip2long($suffix_list[count($suffix_list) - 1]));
3241
		$suffix_list[count($suffix_list) - 1] = substr($hexsuffix, 0, 4);
3242
		$suffix_list[] = substr($hexsuffix, 4, 8);
3243
		$suffix = implode(':', $suffix_list);
3244
	}	
3245
	$prefix = Net_IPv6::uncompress($prefix, true);
3246
	$suffix = Net_IPv6::uncompress($suffix, true);
3247

    
3248
	/*
3249
	 * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
3250
	 *                ^^^^ ^
3251
	 *                |||| \-> 64
3252
	 *                |||\---> 63, 62, 61, 60
3253
	 *                ||\----> 56
3254
	 *                |\-----> 52
3255
	 *                \------> 48
3256
	 */
3257

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

    
3289
	return text_to_compressed_ip6(substr($prefix, 0, $prefix_len) .
3290
	    substr($suffix, $prefix_len));
3291
}
3292

    
3293
function dhcpv6_pd_str_help($pdlen) {
3294
	$result = '';
3295

    
3296
	switch ($pdlen) {
3297
	case 48:
3298
		$result = '::xxxx:xxxx:xxxx:xxxx:xxxx';
3299
		break;
3300
	case 52:
3301
		$result = '::xxx:xxxx:xxxx:xxxx:xxxx';
3302
		break;
3303
	case 56:
3304
		$result = '::xx:xxxx:xxxx:xxxx:xxxx';
3305
		break;
3306
	case 59:
3307
	case 60:
3308
		$result = '::x:xxxx:xxxx:xxxx:xxxx';
3309
		break;
3310
	/*
3311
	 * XXX 63, 62 and 61 should use same mask as 60 but if
3312
	 * we let the user choose this bit it can end up out of PD network
3313
	 *
3314
	 * Leave this with the same as 64 for now until we find a way to
3315
	 * let the user choose it. The side-effect is users with PD with one
3316
	 * of these lengths will not be able to setup DHCP server ranges
3317
	 * for full PD size, only for last /64 network
3318
	 */
3319
	case 61:
3320
	case 62:
3321
	case 63:
3322
	case 64:
3323
	default:
3324
		$result = '::xxxx:xxxx:xxxx:xxxx';
3325
		break;
3326
	}
3327

    
3328
	return $result;
3329
}
3330

    
3331
function huawei_rssi_to_string($rssi) {
3332
	$dbm = array();
3333
	$i = 0;
3334
	$dbstart = -113;
3335
	while ($i < 32) {
3336
		$dbm[$i] = $dbstart + ($i * 2);
3337
		$i++;
3338
	}
3339
	$percent = round(($rssi / 31) * 100);
3340
	$string = "rssi:{$rssi} level:{$dbm[$rssi]}dBm percent:{$percent}%";
3341
	return $string;
3342
}
3343

    
3344
function huawei_mode_to_string($mode, $submode) {
3345
	$modes[0] = gettext("None");
3346
	$modes[1] = "AMPS";
3347
	$modes[2] = "CDMA";
3348
	$modes[3] = "GSM/GPRS";
3349
	$modes[4] = "HDR";
3350
	$modes[5] = "WCDMA";
3351
	$modes[6] = "GPS";
3352

    
3353
	$submodes[0] = gettext("No Service");
3354
	$submodes[1] = "GSM";
3355
	$submodes[2] = "GPRS";
3356
	$submodes[3] = "EDGE";
3357
	$submodes[4] = "WCDMA";
3358
	$submodes[5] = "HSDPA";
3359
	$submodes[6] = "HSUPA";
3360
	$submodes[7] = "HSDPA+HSUPA";
3361
	$submodes[8] = "TD-SCDMA";
3362
	$submodes[9] = "HSPA+";
3363
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3364
	return $string;
3365
}
3366

    
3367
function huawei_service_to_string($state) {
3368
	$modes[0] = gettext("No Service");
3369
	$modes[1] = gettext("Restricted Service");
3370
	$modes[2] = gettext("Valid Service");
3371
	$modes[3] = gettext("Restricted Regional Service");
3372
	$modes[4] = gettext("Powersaving Service");
3373
	$modes[255] = gettext("Unknown Service");
3374
	$string = $modes[$state];
3375
	return $string;
3376
}
3377

    
3378
function huawei_simstate_to_string($state) {
3379
	$modes[0] = gettext("Invalid SIM/locked State");
3380
	$modes[1] = gettext("Valid SIM State");
3381
	$modes[2] = gettext("Invalid SIM CS State");
3382
	$modes[3] = gettext("Invalid SIM PS State");
3383
	$modes[4] = gettext("Invalid SIM CS/PS State");
3384
	$modes[255] = gettext("Missing SIM State");
3385
	$string = $modes[$state];
3386
	return $string;
3387
}
3388

    
3389
function zte_rssi_to_string($rssi) {
3390
	return huawei_rssi_to_string($rssi);
3391
}
3392

    
3393
function zte_mode_to_string($mode, $submode) {
3394
	$modes[0] = gettext("No Service");
3395
	$modes[1] = gettext("Limited Service");
3396
	$modes[2] = "GPRS";
3397
	$modes[3] = "GSM";
3398
	$modes[4] = "UMTS";
3399
	$modes[5] = "EDGE";
3400
	$modes[6] = "HSDPA";
3401

    
3402
	$submodes[0] = "CS_ONLY";
3403
	$submodes[1] = "PS_ONLY";
3404
	$submodes[2] = "CS_PS";
3405
	$submodes[3] = "CAMPED";
3406
	$string = "{$modes[$mode]}, {$submodes[$submode]} " . gettext("Mode");
3407
	return $string;
3408
}
3409

    
3410
function zte_service_to_string($service) {
3411
	$modes[0] = gettext("Initializing Service");
3412
	$modes[1] = gettext("Network Lock error Service");
3413
	$modes[2] = gettext("Network Locked Service");
3414
	$modes[3] = gettext("Unlocked or correct MCC/MNC Service");
3415
	$string = $modes[$service];
3416
	return $string;
3417
}
3418

    
3419
function zte_simstate_to_string($state) {
3420
	$modes[0] = gettext("No action State");
3421
	$modes[1] = gettext("Network lock State");
3422
	$modes[2] = gettext("(U)SIM card lock State");
3423
	$modes[3] = gettext("Network Lock and (U)SIM card Lock State");
3424
	$string = $modes[$state];
3425
	return $string;
3426
}
3427

    
3428
function get_configured_pppoe_server_interfaces() {
3429
	$iflist = array();
3430
	foreach (config_get_path('pppoes/pppoe', []) as $pppoe) {
3431
		if ($pppoe['mode'] == "server") {
3432
			$int = "poes". $pppoe['pppoeid'];
3433
			$iflist[$int] = strtoupper($int);
3434
		}
3435
	}
3436
	return $iflist;
3437
}
3438

    
3439
function get_pppoes_child_interfaces($ifpattern) {
3440
	$if_arr = array();
3441
	if ($ifpattern == "") {
3442
		return;
3443
	}
3444

    
3445
	exec("/sbin/ifconfig", $out, $ret);
3446
	foreach ($out as $line) {
3447
		if (preg_match("/^({$ifpattern}-[0-9]+):/i", $line, $match)) {
3448
			$if_arr[] = $match[1];
3449
		}
3450
	}
3451
	return $if_arr;
3452

    
3453
}
3454

    
3455
/****f* pfsense-utils/pkg_call_plugins
3456
 * NAME
3457
 *   pkg_call_plugins
3458
 * INPUTS
3459
 *   $plugin_type value used to search in package configuration if the plugin is used, also used to create the function name
3460
 *   $plugin_params parameters to pass to the plugin function for passing multiple parameters a array can be used.
3461
 * RESULT
3462
 *   returns associative array results from the plugin calls for each package
3463
 * NOTES
3464
 *   This generic function can be used to notify or retrieve results from functions that are defined in packages.
3465
 ******/
3466
function pkg_call_plugins($plugin_type, $plugin_params) {
3467
	global $g;
3468
	$results = array();
3469
	foreach (config_get_path('installedpackages/package') as $package) {
3470
		if (is_array($package['plugins']['item'])) {
3471
			foreach ($package['plugins']['item'] as $plugin) {
3472
				if ($plugin['type'] == $plugin_type) {
3473
					if (file_exists($package['include_file'])) {
3474
						require_once($package['include_file']);
3475
					} else {
3476
						continue;
3477
					}
3478
					$pkgname = substr(reverse_strrchr($package['configurationfile'], "."), 0, -1);
3479
					$plugin_function = $pkgname . '_'. $plugin_type;
3480
					$results[$pkgname] = call_user_func($plugin_function, $plugin_params);
3481
				}
3482
			}
3483
		}
3484
	}
3485
	return $results;
3486
}
3487

    
3488
// Convert IPv6 addresses to lower case
3489
function addrtolower($ip) {
3490
	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
3491
		return(strtolower($ip));
3492
	} else {
3493
		return($ip);
3494
	}
3495
}
3496

    
3497
function compare_by_name($a, $b) {
3498
	return strcasecmp($a['name'], $b['name']);
3499
}
3500

    
3501
/****f* pfsense-utils/getarraybyref
3502
 * NAME
3503
 *   getarraybyref
3504
 * INPUTS
3505
 *   $array the array of which a items array needs to be found.
3506
 *   $args.. the sub-items to be retrieved/created
3507
 * RESULT
3508
 *   returns the array that was retrieved from configuration, its created if it does not exist
3509
 * NOTES
3510
 *   Used by haproxy / acme / others.?. .
3511
 *   can be used like this:  $a_certificates = getarraybyref($config, 'installedpackages', 'acme', 'certificates', 'item');
3512
 ******/
3513
function &getarraybyref(&$array) {
3514
	if (!isset($array)) {
3515
		return false;
3516
	}
3517
	if (!is_array($array)) {
3518
		$array = array();
3519
	}
3520
	$item = &$array;
3521
	$arg = func_get_args();
3522
	for($i = 1; $i < count($arg); $i++) {
3523
		$itemindex = $arg[$i];
3524
		if (!is_array($item[$itemindex])) {
3525
			$item[$itemindex] = array();
3526
		}
3527
		$item = &$item[$itemindex];
3528
	}
3529
	return $item;
3530
}
3531

    
3532
/****f* pfsense-utils/send_download_data
3533
 * NAME
3534
 *   send_download_data - Send content to a user's browser as a file to download
3535
 * INPUTS
3536
 *   $type        : The type of download, either 'data' to send the contents of
3537
 *                    a variable or 'file' to send the contents of a file on the
3538
 *                    filesystem.
3539
 *   $content     : For 'data' type, the content to send the user. For 'file'
3540
 *                    type, the full path to the file to send.
3541
 *   $userfilename: The filename presented to the user when downloading. For
3542
 *                    'file' type, this may be omitted and the basename of
3543
 *                    $content will be used instead.
3544
 *   $contenttype : MIME content type of the data. Default "application/octet-stream"
3545
 * RESULT
3546
 *   Sends the data to the browser as a file to download.
3547
 ******/
3548

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

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

    
3556
	/* Cannot determine the filename, so bail. */
3557
	if (empty($name)) {
3558
		exit;
3559
	}
3560

    
3561
	/* Send basic download headers */
3562
	header("Content-Type: {$contenttype}");
3563
	header("Content-Length: {$size}");
3564
	header("Content-Disposition: attachment; filename=" . urlencode($name));
3565

    
3566
	/* Send cache headers */
3567
	if (isset($_SERVER['HTTPS'])) {
3568
		header('Pragma: ');
3569
		header('Cache-Control: ');
3570
	} else {
3571
		header("Pragma: private");
3572
		header("Cache-Control: private, must-revalidate");
3573
	}
3574

    
3575
	/* Ensure output buffering is off so PHP does not consume
3576
	 * memory in readfile(). https://redmine.pfsense.org/issues/9239 */
3577
	while (ob_get_level()) {
3578
		@ob_end_clean();
3579
	}
3580

    
3581
	/* Send the data to the user */
3582
	if ($type == 'file') {
3583
		readfile($content);
3584
	} else {
3585
		echo $content;
3586
	}
3587

    
3588
	/* Flush any remaining output buffer */
3589
	@ob_end_flush();
3590
	exit;
3591
}
3592

    
3593
// Test whether the hostname in a URL can be resolved with a very short timeout
3594
function is_url_hostname_resolvable($url) {
3595
	$urlhostname = parse_url($url, PHP_URL_HOST);
3596
	if (empty($urlhostname)) {
3597
		return false;
3598
	}
3599
	putenv("RES_OPTIONS=timeout:3 attempts:1");
3600
	$resolvable = ($urlhostname !== gethostbyname($urlhostname));
3601
	putenv("RES_OPTIONS");
3602
	return $resolvable;
3603
}
3604

    
3605
function get_pf_timeouts () {
3606
	$pftimeout = array();
3607
	exec("/sbin/pfctl -st", $pfctlst, $retval);
3608
	if ($retval == 0) {
3609
		foreach ($pfctlst as $pfst) {
3610
			preg_match('/([a-z]+)\.([a-z]+)\s+([0-9]+)/', $pfst, $timeout);
3611
			if ($timeout[1] == "other") {
3612
				$proto = "Other";
3613
			} else {
3614
				$proto = strtoupper($timeout[1]);
3615
			}
3616
			if ($timeout[2] == "finwait") {
3617
				$type = "FIN Wait";
3618
			} else {
3619
				$type = ucfirst($timeout[2]);
3620
			}
3621
			$pftimeout[$proto][$type]['name'] = $proto . " " . $type;
3622
			$pftimeout[$proto][$type]['keyname'] = $timeout[1] . $timeout[2] . "timeout";
3623
			$pftimeout[$proto][$type]['value'] = $timeout[3];
3624
		}
3625
	}
3626
	return $pftimeout;
3627
}
3628

    
3629
function set_curlproxy(&$ch) {
3630
	if (!empty(config_get_path('system/proxyurl'))) {
3631
		curl_setopt($ch, CURLOPT_PROXY, config_get_path('system/proxyurl'));
3632
		if (!empty(config_get_path('system/proxyport'))) {
3633
			curl_setopt($ch, CURLOPT_PROXYPORT, config_get_path('system/proxyport'));
3634
		}
3635
		$proxyuser = config_get_path('system/proxyuser');
3636
		$proxypass = config_get_path('system/proxypass');
3637
		if (!empty($proxyuser) && !empty($proxypass)) {
3638
			@curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
3639
			curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$proxyuser}:{$proxypass}");
3640
		}
3641
	}
3642
}
3643
?>
(39-39/61)