Project

General

Profile

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

    
25
require_once("globals.inc");
26
require_once("service-utils.inc");
27
require_once("/usr/local/www/includes/functions.inc.php");
28

    
29
if (file_exists("/cf/conf/use_xmlreader")) {
30
	require_once("xmlreader.inc");
31
} else {
32
	require_once("xmlparse.inc");
33
}
34

    
35
require_once("pfsense-utils.inc");
36

    
37
if (!function_exists("pkg_debug")) {
38
	/* set up logging if needed */
39
	function pkg_debug($msg) {
40
		global $g, $debug, $fd_log;
41

    
42
		if (!$debug) {
43
			return;
44
		}
45

    
46
		if (!$fd_log) {
47
			$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log",
48
			    "w");
49
		}
50

    
51
		if (!$fd_log) {
52
			update_status(gettext("Warning, could not open log " .
53
			    "for writing.") . "\n");
54
			return;
55
		}
56
		@fwrite($fd_log, $msg);
57
	}
58
}
59

    
60
/* Validate if pkg name is valid */
61
function pkg_valid_name($pkgname) {
62
	global $g;
63

    
64
	$pattern = "/^{$g['pkg_prefix']}[a-zA-Z0-9\.\-_]+$/";
65
	return preg_match($pattern, $pkgname);
66
}
67

    
68
/* Remove pkg_prefix from package name if it's present */
69
function pkg_remove_prefix(&$pkg_name) {
70
	global $g;
71

    
72
	if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) ==
73
	    $g['pkg_prefix']) {
74
		$pkg_name = substr($pkg_name, strlen($g['pkg_prefix']));
75
	}
76
}
77

    
78
/* Execute pkg update when it's necessary */
79
function pkg_update($force = false) {
80
	global $g;
81

    
82
	return pkg_call("update" . ($force ? " -f" : ""));
83
}
84

    
85
/* return an array with necessary environment vars for pkg */
86
function pkg_env($extra_env = array()) {
87
	global $config, $g;
88

    
89
	$user_agent = $g['product_label'] . '/' . $g['product_version'];
90
	if (!isset($config['system']['do_not_send_uniqueid'])) {
91
		$user_agent .= ':' . system_get_uniqueid();
92
	}
93

    
94
	$pkg_env_vars = array(
95
		"LANG" => "C",
96
		"HTTP_USER_AGENT" => $user_agent,
97
		"ASSUME_ALWAYS_YES" => "true",
98
		"FETCH_TIMEOUT" => 5,
99
		"FETCH_RETRY" => 2
100
	);
101

    
102
	if (!empty($config['system']['proxyurl'])) {
103
		$http_proxy = $config['system']['proxyurl'];
104
		if (!empty($config['system']['proxyport'])) {
105
			$http_proxy .= ':' . $config['system']['proxyport'];
106
		}
107
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
108

    
109
		if (!empty($config['system']['proxyuser']) &&
110
		    !empty($config['system']['proxypass'])) {
111
			$pkg_env_vars['HTTP_PROXY_AUTH'] = "basic:*:" .
112
			    $config['system']['proxyuser'] . ":" .
113
			    $config['system']['proxypass'];
114
		}
115
	}
116

    
117
	if (isset($config['system']['use_mfs_tmpvar']) &&
118
	    !file_exists("/conf/ram_disks_failed")) {
119
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
120
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
121
	}
122

    
123
	foreach ($extra_env as $key => $value) {
124
		$pkg_env_vars[$key] = $value;
125
	}
126

    
127
	return $pkg_env_vars;
128
}
129

    
130
/* Execute a pkg call */
131
function pkg_call($params, $mute = false, $extra_env = array()) {
132
	global $g, $config;
133

    
134
	if (empty($params)) {
135
		return false;
136
	}
137

    
138
	$descriptorspec = array(
139
		1 => array("pipe", "w"), /* stdout */
140
		2 => array("pipe", "w")	 /* stderr */
141
	);
142

    
143
	pkg_debug("pkg_call(): {$params}\n");
144
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
145
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
146

    
147
	if (!is_resource($process)) {
148
		return false;
149
	}
150

    
151
	stream_set_blocking($pipes[1], 0);
152
	stream_set_blocking($pipes[2], 0);
153

    
154
	/* XXX: should be a tunable? */
155
	$timeout = 60; // seconds
156
	$error_log = '';
157

    
158
	do {
159
		$write = array();
160
		$read = array($pipes[1], $pipes[2]);
161
		$except = array();
162

    
163
		$stream = @stream_select($read, $write, $except, $timeout);
164
		if ($stream !== FALSE && $stream > 0) {
165
			foreach ($read as $pipe) {
166
				$content = stream_get_contents($pipe);
167
				if ($content == '') {
168
					continue;
169
				}
170
				if ($pipe === $pipes[1]) {
171
					if (!$mute) {
172
						update_status($content);
173
					}
174
					flush();
175
				} else if ($pipe === $pipes[2]) {
176
					$error_log .= $content;
177
				}
178
			}
179
		}
180

    
181
		$status = proc_get_status($process);
182
	} while ($status['running']);
183

    
184
	fclose($pipes[1]);
185
	fclose($pipes[2]);
186
	proc_close($process);
187

    
188

    
189
	$rc = $status['exitcode'];
190

    
191
	pkg_debug("pkg_call(): rc = {$rc}\n");
192
	if ($rc == 0) {
193
		return true;
194
	}
195

    
196
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
197
	if (!$mute) {
198
		update_status("\n\n" . sprintf(gettext("ERROR!!! An error " .
199
		    "occurred on pkg execution (rc = %d) with parameters " .
200
		    "'%s':"), $rc, $params) . "\n" . $error_log . "\n");
201
	}
202

    
203
	return false;
204
}
205

    
206
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
207
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
208
	global $g, $config;
209

    
210
	if (empty($params)) {
211
		return -1;
212
	}
213

    
214
	$descriptorspec = array(
215
		1 => array("pipe", "w"), /* stdout */
216
		2 => array("pipe", "w")	 /* stderr */
217
	);
218

    
219

    
220
	pkg_debug("pkg_exec(): {$params}\n");
221
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
222
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
223

    
224
	if (!is_resource($process)) {
225
		return -1;
226
	}
227

    
228
	$stdout = '';
229
	while (($l = fgets($pipes[1])) !== FALSE) {
230
		$stdout .= $l;
231
	}
232
	fclose($pipes[1]);
233

    
234
	$stderr = '';
235
	while (($l = fgets($pipes[2])) !== FALSE) {
236
		$stderr .= $l;
237
	}
238
	fclose($pipes[2]);
239

    
240

    
241
	return proc_close($process);
242
}
243

    
244
/* Compare 2 pkg versions and return:
245
 * '=' - versions are the same
246
 * '>' - $v1 > $v2
247
 * '<' - $v1 < $v2
248
 * '?' - Error
249
 */
250
function pkg_version_compare($v1, $v2) {
251
	if (empty($v1) || empty($v2)) {
252
		return '?';
253
	}
254

    
255
	$rc = pkg_exec("version -t '{$v1}' '{$v2}'", $stdout, $stderr);
256

    
257
	if ($rc != 0) {
258
		return '?';
259
	}
260

    
261
	return str_replace("\n", "", $stdout);
262
}
263

    
264
/* Check if package is installed */
265
function is_pkg_installed($pkg_name) {
266
	global $g;
267

    
268
	if (empty($pkg_name)) {
269
		return false;
270
	}
271

    
272
	return pkg_call("info -e " . $pkg_name, true);
273
}
274

    
275
/* Install package, $pkg_name should not contain prefix */
276
function pkg_install($pkg_name, $force = false) {
277
	global $g;
278
	$result = false;
279

    
280
	$shortname = $pkg_name;
281
	pkg_remove_prefix($shortname);
282

    
283
	$pkg_force = "";
284
	if ($force) {
285
		$pkg_force = "-f ";
286
	}
287

    
288
	pkg_debug("Installing package {$shortname}\n");
289
	if ($force || !is_pkg_installed($pkg_name)) {
290
		$result = pkg_call("install -y " . $pkg_force . $pkg_name);
291
		/* Cleanup cache to free disk space */
292
		pkg_call("clean -y");
293
	}
294

    
295
	return $result;
296
}
297

    
298
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
299
function pkg_delete($pkg_name) {
300
	global $g;
301

    
302
	$shortname = $pkg_name;
303
	pkg_remove_prefix($shortname);
304

    
305
	pkg_debug("Removing package {$shortname}\n");
306
	if (is_pkg_installed($pkg_name)) {
307
		pkg_call("delete -y " . $pkg_name);
308
		/* Cleanup unnecessary dependencies */
309
		pkg_call("autoremove -y");
310
	}
311
}
312

    
313
/* Check if package is present in config.xml */
314
function is_package_installed($package_name) {
315
	return (get_package_id($package_name) != -1);
316
}
317

    
318
/* Find package array index */
319
function get_package_id($package_name) {
320
	global $config;
321

    
322
	if (!isset($config['installedpackages']['package']) ||
323
	    !is_array($config['installedpackages']['package'])) {
324
		return -1;
325
	}
326

    
327
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
328
		if ($pkg['name'] == $package_name ||
329
		    get_package_internal_name($pkg) == $package_name) {
330
			return $idx;
331
		}
332
	}
333

    
334
	return -1;
335
}
336

    
337
/* Return internal_name when it's defined, otherwise, returns name */
338
function get_package_internal_name($package_data) {
339
	if (isset($package_data['internal_name']) &&
340
	    ($package_data['internal_name'] != "")) {
341
		/* e.g. name is Ipguard-dev, internal name is ipguard */
342
		return $package_data['internal_name'];
343
	} else {
344
		return $package_data['name'];
345
	}
346
}
347

    
348
// Get information about packages.
349
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false,
350
    $installed_pkgs_only = false) {
351
	global $g, $input_errors;
352

    
353
	$out = $err = $extra_param = '';
354
	$rc = 0;
355

    
356
	unset($pkg_filter);
357

    
358
	if (is_array($pkgs)) {
359
		$pkg_filter = $pkgs;
360
		$pkgs = $g['pkg_prefix'] . '*';
361
	} elseif ($pkgs == 'all') {
362
		$pkgs = $g['pkg_prefix'] . '*';
363
	}
364

    
365
	$base_packages = (substr($pkgs, 0, strlen($g['pkg_prefix'])) !=
366
	    $g['pkg_prefix']);
367

    
368
	if ($installed_pkgs_only && !is_pkg_installed($pkgs)) {
369
		/*
370
		 * Return early if the caller wants just installed packages
371
		 * and there are none.  Saves doing any calls that might
372
		 * access a remote package repo.
373
		 */
374
		return array();
375
	}
376

    
377
	if (!function_exists('is_subsystem_dirty')) {
378
		require_once("util.inc");
379
	}
380

    
381
	/* Do not run remote operations if pkg has a lock */
382
	if (is_subsystem_dirty('pkg')) {
383
		$remote_repo_usage_disabled = true;
384
		$lock = false;
385
	} else {
386
		$lock = true;
387
	}
388

    
389
	if ($lock) {
390
		mark_subsystem_dirty('pkg');
391
	}
392

    
393
	if ($remote_repo_usage_disabled) {
394
		$extra_param = "-U ";
395
	}
396

    
397
	$did_search = false;
398
	$search_rc = 0;
399
	$info_rc = 0;
400
	$search_items = array();
401
	$info_items = array();
402

    
403
	if ($base_packages) {
404
		$repo_param = "";
405
	} else {
406
		$repo_param = "-r {$g['product_name']}";
407
	}
408

    
409
	/*
410
	 * If we want more than just the currently installed packages or
411
	 * we want up-to-date remote repo info then do a full pkg search
412
	 */
413
	if (!$installed_pkgs_only || !$remote_repo_usage_disabled) {
414
		$did_search = true;
415
		$search_rc = pkg_exec("search {$repo_param} " .
416
		    "{$extra_param}-R --raw-format json-compact " .
417
		    $pkgs, $search_out, $search_err);
418
		if ($search_rc == 0) {
419
			$search_items = explode("\n", chop($search_out));
420
			array_walk($search_items, function(&$v, &$k) {
421
				$v = json_decode($v, true);
422
			});
423
		}
424
	}
425

    
426
	/*
427
	 * We always should look for local items to detect packages that
428
	 * were removed from remote repo but are already installed locally
429
	 *
430
	 * Take pkg search return code into consideration to fallback to local
431
	 * information when remote repo is not accessible
432
	 */
433
	if (is_pkg_installed($pkgs) || $search_rc != 0) {
434
		$info_rc = pkg_exec("info -R --raw-format json-compact " .
435
		    $pkgs, $info_out, $info_err);
436
		if ($info_rc == 0) {
437
			$info_items = explode("\n", chop($info_out));
438
			array_walk($info_items, function(&$v, &$k) {
439
				$v = json_decode($v, true);
440
			});
441
		}
442
	}
443

    
444
	if ($lock) {
445
		clear_subsystem_dirty('pkg');
446
	}
447

    
448
	if ($search_rc != 0 && $info_rc != 0) {
449
		update_status("\n" . gettext(
450
		    "ERROR: Error trying to get packages list. Aborting...")
451
		    . "\n");
452
		update_status($search_err . "\n" . $info_err);
453
		$input_errors[] = gettext(
454
		    "ERROR: Error trying to get packages list. Aborting...") .
455
		    "\n";
456
		$input_errors[] = $search_err . "\n" . $info_err;
457
		return array();
458
	}
459

    
460
	/* It was not possible to search, use local information only */
461
	if ($search_rc != 0 || !$did_search) {
462
		$search_items = $info_items;
463
	} else {
464
		foreach ($info_items as $pkg_info) {
465
			if (empty($pkg_info['name'])) {
466
				continue;
467
			}
468

    
469
			if (array_search($pkg_info['name'], array_column(
470
			    $search_items, 'name')) === FALSE) {
471
				$pkg_info['obsolete'] = true;
472
				$search_items[] = $pkg_info;
473
			}
474
		}
475
	}
476

    
477
	$result = array();
478
	foreach ($search_items as $pkg_info) {
479
		if (empty($pkg_info['name'])) {
480
			continue;
481
		}
482

    
483
		if (isset($pkg_filter) && !in_array($pkg_info['name'],
484
		    $pkg_filter)) {
485
			continue;
486
		}
487

    
488
		$pkg_info['shortname'] = $pkg_info['name'];
489
		pkg_remove_prefix($pkg_info['shortname']);
490

    
491
		/* XXX: Add it to globals.inc? */
492
		$pkg_info['changeloglink'] =
493
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
494
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
495

    
496
		$pkg_is_installed = false;
497

    
498
		if (is_pkg_installed($pkg_info['name'])) {
499
			$rc = pkg_exec("query %R {$pkg_info['name']}", $out,
500
			    $err);
501
			if (!$base_packages &&
502
			    rtrim($out) != $g['product_name']) {
503
				continue;
504
			}
505

    
506
			$pkg_info['installed'] = true;
507
			$pkg_is_installed = true;
508

    
509
			$rc = pkg_exec("query %v {$pkg_info['name']}", $out,
510
			    $err);
511

    
512
			if ($rc != 0) {
513
				update_status("\n" . gettext("ERROR: Error " .
514
				    "trying to get package version. " .
515
				    "Aborting...") . "\n");
516
				update_status($err);
517
				$input_errors[] = gettext("ERROR: Error " .
518
				    "trying to get package version. " .
519
				    "Aborting...") . "\n";
520
				$input_errors[] = $err;
521
				return array();
522
			}
523

    
524
			$pkg_info['installed_version'] = str_replace("\n", "",
525
			    $out);
526

    
527
			/*
528
			 * We used pkg info to collect pkg data so remote
529
			 * version is not available. Lets try to collect it
530
			 * using rquery if possible
531
			 */
532
			if ($search_rc != 0 || !$did_search) {
533
				$rc = pkg_exec(
534
				    "rquery -U %v {$pkg_info['name']}", $out,
535
				    $err);
536

    
537
				if ($rc == 0) {
538
					$pkg_info['version'] =
539
					    str_replace("\n", "", $out);
540
				}
541
			}
542

    
543
		} else if (is_package_installed($pkg_info['shortname'])) {
544
			$pkg_info['broken'] = true;
545
			$pkg_is_installed = true;
546
		}
547

    
548
		$pkg_info['desc'] = preg_replace('/\n+WWW:.*$/', '',
549
		    $pkg_info['desc']);
550

    
551
		if (!$installed_pkgs_only || $pkg_is_installed) {
552
			$result[] = $pkg_info;
553
		}
554
		unset($pkg_info);
555
	}
556

    
557
	/* Sort result alphabetically */
558
	usort($result, function($a, $b) {
559
		return(strcasecmp ($a['name'], $b['name']));
560
	});
561

    
562
	return $result;
563
}
564

    
565
/*
566
 * If binary pkg is installed but post-install tasks were not
567
 * executed yet, do it now.
568
 * This scenario can happen when a pkg is pre-installed during
569
 * build phase, and at this point, cannot find a running system
570
 * to register itself in config.xml and also execute custom
571
 * install functions
572
 */
573
function register_all_installed_packages() {
574
	global $g, $config, $pkg_interface;
575

    
576
	$pkg_info = get_pkg_info('all', true, true);
577

    
578
	foreach ($pkg_info as $pkg) {
579
		pkg_remove_prefix($pkg['name']);
580

    
581
		if (is_package_installed($pkg['name'])) {
582
			continue;
583
		}
584

    
585
		update_status(sprintf(gettext(
586
		    "Running last steps of %s installation.") . "\n",
587
		    $pkg['name']));
588
		install_package_xml($pkg['name']);
589
	}
590
}
591

    
592
/*
593
 * resync_all_package_configs() Force packages to setup their configuration
594
 * and rc.d files.  This function may also print output to the terminal
595
 * indicating progress.
596
 */
597
function resync_all_package_configs($show_message = false) {
598
	global $config, $pkg_interface, $g;
599

    
600
	log_error(gettext("Resyncing configuration for all packages."));
601

    
602
	if (!isset($config['installedpackages']['package']) ||
603
	    !is_array($config['installedpackages']['package'])) {
604
		return;
605
	}
606

    
607
	if ($show_message == true) {
608
		echo "Syncing packages:";
609
	}
610

    
611

    
612
	foreach ($config['installedpackages']['package'] as $idx => $package) {
613
		if (empty($package['name'])) {
614
			continue;
615
		}
616
		if ($show_message == true) {
617
			echo " " . $package['name'];
618
		}
619
		if (platform_booting() != true) {
620
			stop_service(get_package_internal_name($package));
621
		}
622
		sync_package($package['name']);
623
		update_status(gettext("Syncing packages...") . "\n");
624
	}
625

    
626
	if ($show_message == true) {
627
		echo " done.\n";
628
	}
629
}
630

    
631
function uninstall_package($package_name) {
632
	global $config;
633

    
634
	$internal_name = $package_name;
635
	$id = get_package_id($package_name);
636
	if ($id >= 0) {
637
		$internal_name = get_package_internal_name(
638
		    $config['installedpackages']['package'][$id]);
639
		stop_service($internal_name);
640
	}
641
	$pkg_name = $g['pkg_prefix'] . $internal_name;
642

    
643
	if (is_pkg_installed($pkg_name)) {
644
		update_status(gettext("Removing package...") . "\n");
645
		pkg_delete($pkg_name);
646
	} else {
647
		delete_package_xml($package_name);
648
	}
649

    
650
	update_status(gettext("done.") . "\n");
651
}
652

    
653
function reinstall_package($package_name) {
654
	global $config, $g;
655

    
656
	$internal_name = $package_name;
657
	$id = get_package_id($package_name);
658
	if ($id >= 0) {
659
		$internal_name = get_package_internal_name(
660
		    $config['installedpackages']['package'][$id]);
661
	}
662
	$pkg_name = $g['pkg_prefix'] . $internal_name;
663
	pkg_install($pkg_name);
664
}
665

    
666
/* Run <custom_php_resync_config_command> */
667
function sync_package($package_name) {
668
	global $config, $builder_package_install;
669

    
670
	// If this code is being called by pfspkg_installer
671
	// which the builder system uses then return (ignore).
672
	if ($builder_package_install) {
673
		return;
674
	}
675

    
676
	if (empty($config['installedpackages']['package'])) {
677
		return;
678
	}
679

    
680
	if (($pkg_id = get_package_id($package_name)) == -1) {
681
		// This package doesn't really exist - exit the function.
682
		return;
683
	}
684

    
685
	if (!is_array($config['installedpackages']['package'][$pkg_id])) {
686
		// No package belongs to the pkg_id passed to this function.
687
		return;
688
	}
689

    
690
	$package = &$config['installedpackages']['package'][$pkg_id];
691
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
692
		log_error(sprintf(gettext("The %s package is missing its " .
693
		    "configuration file and must be reinstalled."),
694
		    $package['name']));
695
		delete_package_xml($package['name']);
696
		return;
697
	}
698

    
699
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
700
	    $package['configurationfile'], "packagegui");
701
	if (isset($pkg_config['nosync'])) {
702
		return;
703
	}
704

    
705
	/* Bring in package include files */
706
	if (!empty($pkg_config['include_file'])) {
707
		$include_file = $pkg_config['include_file'];
708
		if (file_exists($include_file)) {
709
			require_once($include_file);
710
		} else {
711
			log_error(sprintf(gettext('Reinstalling package %1$s " .
712
			    "because its include file(%2$s) is missing!'),
713
			    $package['name'], $include_file));
714
			uninstall_package($package['name']);
715
			if (reinstall_package($package['name']) != 0) {
716
				log_error(sprintf(gettext("Reinstalling " .
717
				    "package %s failed. Take appropriate " .
718
				    "measures!!!"), $package['name']));
719
				return;
720
			}
721
			if (file_exists($include_file)) {
722
				require_once($include_file);
723
			} else {
724
				return;
725
			}
726
		}
727
	}
728

    
729
	if (!empty($pkg_config['custom_php_global_functions'])) {
730
		eval($pkg_config['custom_php_global_functions']);
731
	}
732
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
733
		eval($pkg_config['custom_php_resync_config_command']);
734
	}
735
}
736

    
737
/* Read info.xml installed by package and return an array */
738
function read_package_config($package_name) {
739
	global $g;
740

    
741
	$pkg_info_xml = '/usr/local/share/' . $g['pkg_prefix'] . $package_name .
742
	    '/info.xml';
743

    
744
	if (!file_exists($pkg_info_xml)) {
745
		return false;
746
	}
747

    
748
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
749

    
750
	if (empty($pkg_info)) {
751
		return false;
752
	}
753

    
754
	/* it always returns an array with 1 item */
755
	return $pkg_info['package'][0];
756
}
757

    
758
/* Read package configurationfile and return an array */
759
function read_package_configurationfile($package_name) {
760
	global $config, $g;
761

    
762
	$pkg_config = array();
763
	$id = get_package_id($package_name);
764

    
765
	if ($id < 0 || !isset($config['installedpackages']['package'][$id])) {
766
		return $pkg_config;
767
	}
768

    
769
	$pkg_data = $config['installedpackages']['package'][$id];
770

    
771
	if (empty($pkg_data['configurationfile'])) {
772
		return $pkg_config;
773
	}
774

    
775
	if (!file_exists('/usr/local/pkg/' . $pkg_data['configurationfile'])) {
776
		return $pkg_config;
777
	}
778

    
779
	$pkg_config = parse_xml_config_pkg('/usr/local/pkg/' .
780
	    $pkg_data['configurationfile'], "packagegui");
781

    
782
	return $pkg_config;
783
}
784

    
785
function get_after_install_info($package_name) {
786
	$pkg_config = read_package_config($package_name);
787

    
788
	if (isset($pkg_config['after_install_info'])) {
789
		return $pkg_config['after_install_info'];
790
	}
791

    
792
	return '';
793
}
794

    
795
function eval_once($toeval) {
796
	global $evaled;
797
	if (!$evaled) {
798
		$evaled = array();
799
	}
800
	$evalmd5 = md5($toeval);
801
	if (!in_array($evalmd5, $evaled)) {
802
		@eval($toeval);
803
		$evaled[] = $evalmd5;
804
	}
805
	return;
806
}
807

    
808
function install_package_xml($package_name) {
809
	global $g, $config, $pkg_interface;
810

    
811
	if (($pkg_info = read_package_config($package_name)) == false) {
812
		return false;
813
	}
814

    
815
	pkg_debug(gettext("Beginning package installation.") . "\n");
816
	log_error(sprintf(gettext('Beginning package installation for %s .'),
817
	    $pkg_info['name']));
818

    
819
	/* add package information to config.xml */
820
	$pkgid = get_package_id($pkg_info['name']);
821
	update_status(gettext("Saving updated package information...") . "\n");
822
	if ($pkgid == -1) {
823
		init_config_arr(array('installedpackages', 'package'));
824
		$config['installedpackages']['package'][] = $pkg_info;
825
		$changedesc = sprintf(gettext("Installed %s package."),
826
		    $pkg_info['name']);
827
		$to_output = gettext("done.") . "\n";
828
	} else {
829
		init_config_arr(array('installedpackages', 'package', $pkgid));
830
		$config['installedpackages']['package'][$pkgid] = $pkg_info;
831
		$changedesc = sprintf(gettext("Overwrote previous " .
832
		    "installation of %s."), $pkg_info['name']);
833
		$to_output = gettext("overwrite!") . "\n";
834
	}
835
	write_config(sprintf(gettext("Intermediate config write during " .
836
	    "package install for %s."), $pkg_info['name']));
837
	update_status($to_output);
838

    
839
	if (($pkgid = get_package_id($package_name)) == -1) {
840
		update_status(sprintf(gettext('The %1$s package is not " .
841
		    "installed.%2$sInstallation aborted.'), $package_name,
842
		    "\n\n"));
843

    
844
		uninstall_package($package_name);
845
		write_config($changedesc);
846
		log_error(sprintf(gettext("Failed to install package: %s."),
847
		    $pkg_info['name']));
848
		update_status(gettext("Failed to install package.") . "\n");
849
		return false;
850
	}
851

    
852
	if (!file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
853
		pkg_debug("Unable to find config file\n");
854
		update_status(gettext("Loading package configuration... " .
855
		    "failed!") .  "\n\n" . gettext("Installation aborted."));
856
		pkg_debug(gettext("Unable to load package configuration. " .
857
		    "Installation aborted.") ."\n");
858

    
859
		uninstall_package($package_name);
860
		write_config($changedesc);
861
		log_error(sprintf(gettext("Failed to install package: %s."),
862
		    $pkg_info['name']));
863
		update_status(gettext("Failed to install package.") . "\n");
864
		return false;
865
	}
866

    
867
	update_status(gettext("Loading package configuration... "));
868
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
869
	    $pkg_info['configurationfile'], "packagegui");
870
	update_status(gettext("done.") . "\n");
871
	update_status(gettext("Configuring package components...") .
872
	    "\n");
873
	if (!empty($pkg_config['filter_rules_needed'])) {
874
		$config['installedpackages']['package'][$pkgid]['filter_rule_function'] =
875
		    $pkg_config['filter_rules_needed'];
876
	}
877
	/* modify system files */
878

    
879
	/* if a require exists, include it.  this will
880
	 * show us where an error exists in a package
881
	 * instead of making us blindly guess
882
	 */
883
	$missing_include = false;
884
	if ($pkg_config['include_file'] <> "") {
885
		update_status(gettext("Loading package instructions...") .
886
		    "\n");
887
		if (file_exists($pkg_config['include_file'])) {
888
			pkg_debug("require_once('" .
889
			    $pkg_config['include_file'] . "')\n");
890
			require_once($pkg_config['include_file']);
891
		} else {
892
			pkg_debug("Missing include " .
893
			    "{$pkg_config['include_file']}\n");
894
			$missing_include = true;
895
			update_status(sprintf(gettext("Include %s is missing!"),
896
			    basename($pkg_config['include_file'])) . "\n");
897

    
898
			uninstall_package($package_name);
899
			write_config($changedesc);
900
			log_error(sprintf(gettext(
901
			    "Failed to install package: %s."),
902
			    $pkg_info['name']));
903
			update_status(gettext("Failed to install package.") .
904
			    "\n");
905
			return false;
906
		}
907
	}
908

    
909
	/* custom commands */
910
	update_status(gettext("Custom commands...") . "\n");
911
	if ($missing_include == false) {
912
		if ($pkg_config['custom_php_global_functions'] <> "") {
913
			update_status(gettext(
914
			    "Executing custom_php_global_functions()..."));
915
			eval_once($pkg_config['custom_php_global_functions']);
916
			update_status(gettext("done.") . "\n");
917
		}
918
		if ($pkg_config['custom_php_install_command']) {
919
			update_status(gettext(
920
			    "Executing custom_php_install_command()..."));
921
			eval_once($pkg_config['custom_php_install_command']);
922
			update_status(gettext("done.") . "\n");
923
		}
924
		if ($pkg_config['custom_php_resync_config_command'] <> "") {
925
			update_status(gettext(
926
			    "Executing custom_php_resync_config_command()..."));
927
			eval_once(
928
			    $pkg_config['custom_php_resync_config_command']);
929
			update_status(gettext("done.") . "\n");
930
		}
931
	}
932
	/* sidebar items */
933
	init_config_arr(array('installedpackages', 'menu'));
934
	if (is_array($pkg_config['menu'])) {
935
		update_status(gettext("Menu items... "));
936
		foreach ($pkg_config['menu'] as $menu) {
937
			foreach ($config['installedpackages']['menu'] as
938
			    $amenu) {
939
				if ($amenu['name'] == $menu['name']) {
940
					continue 2;
941
				}
942
			}
943
			$config['installedpackages']['menu'][] = $menu;
944
		}
945
		update_status(gettext("done.") . "\n");
946
	}
947
	/* services */
948
	init_config_arr(array('installedpackages', 'service'));
949
	if (is_array($pkg_config['service'])) {
950
		update_status(gettext("Services... "));
951
		foreach ($pkg_config['service'] as $service) {
952
			foreach ($config['installedpackages']['service'] as
953
			    $aservice) {
954
				if ($aservice['name'] == $service['name']) {
955
					continue 2;
956
				}
957
			}
958
			$config['installedpackages']['service'][] = $service;
959
		}
960
		update_status(gettext("done.") . "\n");
961
	}
962
	if (is_array($pkg_config['tabs'])) {
963
		init_config_arr(array('installedpackages', 'package', $pkgid, 'tabs'));
964
		$config['installedpackages']['package'][$pkgid]['tabs'] = $pkg_config['tabs'];
965
	}
966
	/* plugins */
967
	if (isset($pkg_config['include_file'])) {
968
		$config['installedpackages']['package'][$pkgid]['include_file'] = $pkg_config['include_file'];
969
	}
970
	if (is_array($pkg_config['plugins'])) {
971
		init_config_arr(array('installedpackages', 'package', $pkgid, 'plugins'));
972
		$config['installedpackages']['package'][$pkgid]['plugins'] = $pkg_config['plugins'];
973
	}
974

    
975
	update_status(gettext("Writing configuration... "));
976
	write_config($changedesc);
977
	log_error(sprintf(gettext("Successfully installed package: %s."),
978
	    $pkg_info['name']));
979
	update_status(gettext("done.") . "\n");
980
	if ($pkg_info['after_install_info']) {
981
		update_status($pkg_info['after_install_info']);
982
	}
983

    
984
	/* set up package logging streams */
985
	if ($pkg_info['logging']) {
986
		system_syslogd_start(true);
987
	}
988

    
989
	return true;
990
}
991

    
992
function delete_package_xml($package_name, $when = "post-deinstall") {
993
	global $g, $config, $pkg_interface;
994

    
995

    
996
	$pkgid = get_package_id($package_name);
997
	if ($pkgid == -1) {
998
		update_status(sprintf(gettext('The %1$s package is not " .
999
		    "installed.%2$sDeletion aborted.'), $package_name, "\n\n"));
1000
		ob_flush();
1001
		sleep(1);
1002
		return;
1003
	}
1004
	pkg_debug(sprintf(gettext("Removing %s package... "), $package_name));
1005
	update_status(sprintf(gettext("Removing %s components..."),
1006
	    $package_name) . "\n");
1007
	/* parse package configuration */
1008
	init_config_arr(array('installedpackages', 'package', $pkgid));
1009
	$pkg_info = $config['installedpackages']['package'][$pkgid];
1010
	init_config_arr(array('installedpackages', 'menu'));
1011
	$menus = &$config['installedpackages']['menu'];
1012
	init_config_arr(array('installedpackages', 'service'));
1013
	$services = &$config['installedpackages']['service'];
1014
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
1015
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" .
1016
		    $pkg_info['configurationfile'], "packagegui");
1017
		/* remove menu items */
1018
		if (is_array($pkg_config['menu']) && is_array($menus)) {
1019
			update_status(gettext("Menu items... "));
1020
			foreach ($pkg_config['menu'] as $menu) {
1021
				foreach ($menus as $key => $instmenu) {
1022
					if ($instmenu['name'] ==
1023
					    $menu['name']) {
1024
						unset($menus[$key]);
1025
						break;
1026
					}
1027
				}
1028
			}
1029
			update_status(gettext("done.") . "\n");
1030
		}
1031
		/* remove services */
1032
		if (is_array($pkg_config['service']) && is_array($services)) {
1033
			update_status(gettext("Services... "));
1034
			foreach ($pkg_config['service'] as $service) {
1035
				foreach ($services as $key => $instservice) {
1036
					if ($instservice['name'] !=
1037
					    $service['name']) {
1038
						continue;
1039
					}
1040
					if (platform_booting() != true) {
1041
						stop_service($service['name']);
1042
					}
1043
					if ($service['rcfile']) {
1044
						if (empty($service['prefix'])) {
1045
							$prefix = RCFILEPREFIX;
1046
						} else {
1047
							$prefix =
1048
							    $service['prefix'];
1049
						}
1050
						unlink_if_exists($prefix .
1051
						    $service['rcfile']);
1052
					}
1053
					unset($services[$key]);
1054
				}
1055
			}
1056
			update_status(gettext("done.") . "\n");
1057
		}
1058
		/*
1059
		 * XXX: Otherwise inclusion of config.inc again invalidates
1060
		 *      actions taken.
1061
		 *	Same is done during installation.
1062
		 */
1063
		write_config(sprintf(gettext("Intermediate config write " .
1064
		    "during package removal for %s."), $package_name));
1065

    
1066
		/*
1067
		 * If a require exists, include it. this will
1068
		 * show us where an error exists in a package
1069
		 * instead of making us blindly guess
1070
		 */
1071
		$missing_include = false;
1072
		if ($pkg_config['include_file'] <> "") {
1073
			update_status(gettext("Loading package instructions...")
1074
			    . "\n");
1075
			if (file_exists($pkg_config['include_file'])) {
1076
				pkg_debug("require_once(\"" .
1077
				    "{$pkg_config['include_file']}\")\n");
1078
				require_once($pkg_config['include_file']);
1079
			} else {
1080
				pkg_debug("Missing include " .
1081
				    $pkg_config['include_file'] . "\n");
1082
				$missing_include = true;
1083
				update_status(sprintf(gettext("Include file " .
1084
				    "%s could not be found for inclusion."),
1085
				    basename($pkg_config['include_file'])) .
1086
				    "\n");
1087
			}
1088
		}
1089
		/*
1090
		 * NOTE: It is not possible to handle parse errors on eval.
1091
		 * So we prevent it from being run at all to not interrupt all
1092
		 * the other code.
1093
		 */
1094
		if ($when == "deinstall" && $missing_include == false) {
1095
			/*
1096
			 * evaluate this package's global functions and pre
1097
			 * deinstall commands
1098
			 */
1099
			if ($pkg_config['custom_php_global_functions'] <> "") {
1100
				eval_once($pkg_config['custom_php_global_functions']);
1101
			}
1102
			if ($pkg_config['custom_php_pre_deinstall_command'] <> "") {
1103
				eval_once($pkg_config['custom_php_pre_deinstall_command']);
1104
			}
1105
		}
1106
		/* deinstall commands */
1107
		if ($when == "deinstall" &&
1108
		    $pkg_config['custom_php_deinstall_command'] <> "") {
1109
			update_status(gettext("Deinstall commands... "));
1110
			if ($missing_include == false) {
1111
				eval_once($pkg_config['custom_php_deinstall_command']);
1112
				update_status(gettext("done.") . "\n");
1113
			} else {
1114
				update_status("\n". gettext("Not executing " .
1115
				    "custom deinstall hook because an " .
1116
				    "include is missing.") . "\n");
1117
			}
1118
		}
1119
	}
1120
	/* syslog */
1121
	$need_syslog_restart = false;
1122
	if (is_array($pkg_info['logging']) && !empty($pkg_info['logging']['logfilename'])) {
1123
		update_status(gettext("Syslog entries... "));
1124
		/* remove package-related syslog configuration but retain log data,
1125
		 * see https://redmine.pfsense.org/issues/11846 */
1126
		@unlink_if_exists("{$g['varetc_path']}/syslog.d/" . basename($pkg_info['logging']['logfilename']) . ".conf");
1127
		update_status("done.\n");
1128
		$need_syslog_restart = true;
1129
	}
1130

    
1131
	if ($when == "post-deinstall") {
1132
		/* remove config.xml entries */
1133
		update_status(gettext("Configuration... "));
1134
		unset($config['installedpackages']['package'][$pkgid]);
1135
		update_status(gettext("done.") . "\n");
1136
		write_config(sprintf(gettext("Removed %s package."),
1137
		    $package_name));
1138
		/*
1139
		 * remove package entry from /etc/syslog.conf if needed
1140
		 * this must be done after removing the entries from config.xml
1141
		 */
1142
		if ($need_syslog_restart) {
1143
			system_syslogd_start(true);
1144
		}
1145
	}
1146
}
1147

    
1148
/*
1149
 * Used during upgrade process or restore backup process, verify all
1150
 * packages installed in config.xml and install pkg accordingly
1151
 */
1152
function package_reinstall_all() {
1153
	global $g, $config, $pkg_interface;
1154

    
1155
	if (!isset($config['installedpackages']['package']) ||
1156
	    !is_array($config['installedpackages']['package'])) {
1157
		return true;
1158
	}
1159

    
1160
	/*
1161
	 * Configure default pkg repo for current version instead of
1162
	 * using it from backup, that could be older
1163
	 */
1164
	$default_repo = pkg_get_default_repo();
1165
	$current_repo_path = "";
1166
	if (!empty($config['system']['pkg_repo_conf_path'])) {
1167
		$current_repo_path = $config['system']['pkg_repo_conf_path'];
1168
	}
1169

    
1170
	if ($current_repo_path != $default_repo['path']) {
1171
		$config['system']['pkg_repo_conf_path'] = $default_repo['path'];
1172
		write_config( "Configured default pkg repo after restore");
1173
		pkg_switch_repo($default_repo['path']);
1174
	}
1175

    
1176
	/* wait for internet connection */
1177
	log_error(gettext("Waiting for Internet connection to update pkg " .
1178
	    "metadata and finish package reinstallation"));
1179
	$ntries = 3;
1180
	while ($ntries > 0) {
1181
		if (pkg_update(true)) {
1182
			break;
1183
		}
1184
		sleep(1);
1185
		$ntries--;
1186
	}
1187

    
1188
	if ($ntries == 0) {
1189
		return false;
1190
	}
1191

    
1192
	$package_list = array();
1193
	foreach ($config['installedpackages']['package'] as $package) {
1194
		$package_list[] = get_package_internal_name($package);
1195
	}
1196

    
1197
	if (!empty($package_list)) {
1198
		$pkg_info = get_pkg_info();
1199
	}
1200

    
1201
	foreach ($package_list as $package) {
1202
		$found = false;
1203
		foreach ($pkg_info as $pkg) {
1204
			pkg_remove_prefix($pkg['name']);
1205
			if ($pkg['name'] == $package) {
1206
				pkg_install($g['pkg_prefix'] . $package, true);
1207
				$found = true;
1208
				break;
1209
			}
1210
		}
1211

    
1212
		if (!$found) {
1213
			if (!function_exists("file_notice")) {
1214
				require_once("notices.inc");
1215
			}
1216

    
1217
			file_notice(gettext("Package reinstall"),
1218
			    sprintf(gettext("Package %s does not exist in " .
1219
			    "current %s version and it has been removed."),
1220
			    $package, $g['product_label']));
1221
			uninstall_package($package);
1222
		}
1223
	}
1224

    
1225
	/*
1226
	 * Verify remaining binary packages not present in current config
1227
	 * during backup restore and remove them
1228
	 */
1229
	$installed_packages = get_pkg_info('all', true, true);
1230
	foreach ($installed_packages as $package) {
1231
		$shortname = $package['name'];
1232
		pkg_remove_prefix($shortname);
1233
		if (get_package_id($shortname) != -1) {
1234
			continue;
1235
		}
1236
		pkg_delete($package['name']);
1237
	}
1238

    
1239
	return true;
1240
}
1241

    
1242
function stop_packages() {
1243
	require_once("config.inc");
1244
	require_once("functions.inc");
1245
	require_once("filter.inc");
1246
	require_once("shaper.inc");
1247
	require_once("captiveportal.inc");
1248
	require_once("pkg-utils.inc");
1249
	require_once("pfsense-utils.inc");
1250
	require_once("service-utils.inc");
1251

    
1252
	global $config, $g;
1253

    
1254
	log_error(gettext("Stopping all packages."));
1255

    
1256
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1257
	if (!$rcfiles) {
1258
		$rcfiles = array();
1259
	} else {
1260
		$rcfiles = array_flip($rcfiles);
1261
		if (!$rcfiles) {
1262
			$rcfiles = array();
1263
		}
1264
	}
1265

    
1266
	if (isset($config['installedpackages']['package']) &&
1267
	    is_array($config['installedpackages']['package'])) {
1268
		foreach ($config['installedpackages']['package'] as $package) {
1269
			$internal_name = get_package_internal_name($package);
1270
			if (is_service_running($internal_name)) {
1271
				echo " Stopping package {$package['name']}...";
1272
				stop_service($internal_name);
1273
				echo "done.\n";
1274
				unset($rcfiles[RCFILEPREFIX .
1275
				    strtolower($internal_name) . ".sh"]);
1276
			}
1277
		}
1278
	}
1279

    
1280
	foreach ($rcfiles as $rcfile => $number) {
1281
		$shell = @popen("/bin/sh", "w");
1282
		if ($shell) {
1283
			echo " Stopping {$rcfile}...";
1284
			if (!@fwrite($shell,
1285
			    "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1286
				if ($shell) {
1287
					pclose($shell);
1288
				}
1289
				$shell = @popen("/bin/sh", "w");
1290
			}
1291
			echo "done.\n";
1292
			pclose($shell);
1293
		}
1294
	}
1295
}
1296

    
1297
/* Identify which meta package is installed */
1298
function get_meta_pkg_name() {
1299
	global $g;
1300

    
1301
	/* XXX: Use pkg annotation */
1302
	if (is_pkg_installed($g['product_name'])) {
1303
		return $g['product_name'];
1304
	}
1305
	foreach ($g['alternativemetaports'] as $suffix) {
1306
		if (is_pkg_installed($g['product_name'] . '-' . $suffix)) {
1307
			return $g['product_name'] . '-' . $suffix;
1308
		}
1309
	}
1310
	return false;
1311
}
1312

    
1313
/* Identify which base package is installed */
1314
function get_base_pkg_name() {
1315
	global $g;
1316

    
1317
	/* XXX: Use pkg annotation */
1318
	if (is_pkg_installed($g['product_name'] . '-base-' . $g['product_name'])) {
1319
		return $g['product_name'] . '-base-' . $g['product_name'];
1320
	} else if (is_pkg_installed($g['product_name'] . '-base')) {
1321
		return $g['product_name'] . '-base';
1322
	}
1323
	return false;
1324
}
1325

    
1326
/* Verify if system needs upgrade (meta package or base) */
1327
function get_system_pkg_version($baseonly = false, $use_cache = true) {
1328
	global $g;
1329

    
1330
	if (!check_dnsavailable()) {
1331
		return false;
1332
	}
1333

    
1334
	$cache_file = $g['version_cache_file'];
1335
	$rc_file = $cache_file . '.rc';
1336

    
1337
	$rc = "";
1338
	if ($use_cache && file_exists($rc_file) &&
1339
	    (time()-filemtime($rc_file) < $g['version_cache_refresh'])) {
1340
		$rc = chop(@file_get_contents($rc_file));
1341
	}
1342

    
1343
	if ($rc == "2") {
1344
		$output = @file_get_contents($cache_file);
1345
	} else if ($rc != "0") {
1346
		$output = exec(
1347
		    "/usr/local/sbin/{$g['product_name']}-upgrade -c", $_gc,
1348
		    $rc);
1349

    
1350
		/* Update cache if it succeeded */
1351
		if ($rc == 0 || $rc == 2) {
1352
			@file_put_contents($cache_file, $output);
1353
			@file_put_contents($rc_file, $rc);
1354
		} else {
1355
			return false;
1356
		}
1357
	}
1358

    
1359
	/* pfSense-upgrade returns 2 when there is a new version */
1360
	if ($rc == "2") {
1361
		$new_version = explode(' ', $output)[0];
1362
	}
1363

    
1364
	$base_pkg = get_base_pkg_name();
1365
	$meta_pkg = get_meta_pkg_name();
1366

    
1367
	if (!$base_pkg || !$meta_pkg) {
1368
		return false;
1369
	}
1370

    
1371
	$info = get_pkg_info($base_pkg, true, true);
1372

    
1373
	$pkg_info = array();
1374
	foreach ($info as $item) {
1375
		if ($item['name'] == $base_pkg) {
1376
			$pkg_info = $item;
1377
			break;
1378
		}
1379
	}
1380

    
1381
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1382
	    $pkg_info['installed_version']))) {
1383
		$info = get_pkg_info($meta_pkg, true, true);
1384

    
1385
		foreach ($info as $item) {
1386
			if ($item['name'] == $meta_pkg) {
1387
				$pkg_info = $item;
1388
				break;
1389
			}
1390
		}
1391
	}
1392

    
1393
	if (empty($pkg_info)) {
1394
		return false;
1395
	}
1396

    
1397
	$result = array(
1398
	    'version'           => $new_version ?: $pkg_info['version'],
1399
	    'installed_version' => $pkg_info['installed_version']
1400
	);
1401

    
1402
	$result['pkg_version_compare'] = pkg_version_compare(
1403
	    $result['installed_version'], $result['version']);
1404

    
1405
	return $result;
1406
}
1407

    
1408
/* List available repos */
1409
function pkg_list_repos() {
1410
	global $g;
1411

    
1412
	$path = "/usr/local/share/{$g['product_name']}/pkg/repos";
1413

    
1414
	$default_descr = @file_get_contents($path .
1415
	    "/{$g['product_name']}-repo.descr");
1416

    
1417
	$default = array(
1418
	    'name' => 'Default',
1419
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1420
	    'descr' => $default_descr
1421
	);
1422

    
1423
	$result = array($default);
1424

    
1425
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1426
	foreach ($conf_files as $conf_file) {
1427
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1428
		if (file_exists($descr_file)) {
1429
			$descr_content = file($descr_file);
1430
			$descr = chop($descr_content[0]);
1431
		} else {
1432
			$descr = 'Unknown';
1433
		}
1434
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1435
			continue;
1436
		}
1437
		$entry = array(
1438
		    'name' => ucfirst(strtolower($matches[1])),
1439
		    'path' => $conf_file,
1440
		    'descr' => $descr
1441
		);
1442
		if (file_exists($conf_file . ".default")) {
1443
			$entry['default'] = true;
1444
		}
1445
		$result[] = $entry;
1446
	}
1447

    
1448
	return $result;
1449
}
1450

    
1451
function pkg_get_default_repo() {
1452
	$repos = pkg_list_repos();
1453

    
1454
	foreach ($repos as $repo) {
1455
		if (isset($repo['default'])) {
1456
			return $repo;
1457
		}
1458
	}
1459

    
1460
	/* No default found, return the first one */
1461
	return ($repos[0]);
1462
}
1463

    
1464
/* List available repos on a format to be used by selectors */
1465
function pkg_build_repo_list() {
1466
	$repos = pkg_list_repos();
1467
	$list = array();
1468

    
1469
	foreach ($repos as $repo) {
1470
		$list[$repo['name']] = $repo['descr'];
1471
	}
1472

    
1473
	return($list);
1474
}
1475

    
1476
/* Find repo by path */
1477
function pkg_get_repo_name($path) {
1478
	$repos = pkg_list_repos();
1479

    
1480
	$default = $repos[0]['name'];
1481
	foreach ($repos as $repo) {
1482
		if ($repo['path'] == $path) {
1483
			return $repo['name'];
1484
		}
1485
		if (isset($repo['default'])) {
1486
			$default = $repo['name'];
1487
		}
1488
	}
1489

    
1490
	/* Default */
1491
	return $default;
1492
}
1493

    
1494
/* Setup pkg.conf according current repo */
1495
function pkg_conf_setup($repo_path) {
1496
	global $g;
1497

    
1498
	$pkg_conf_path = "/usr/local/etc/pkg.conf";
1499
	$conf = "/usr/local/etc/pkg/repos/{$g['product_name']}.conf";
1500
	if (!file_exists($conf)) {
1501
		return;
1502
	}
1503

    
1504
	$real_conf = readlink($conf);
1505

    
1506
	if (!$real_conf) {
1507
		return;
1508
	}
1509

    
1510
	$abi_file = str_replace('.conf', '.abi', $real_conf);
1511
	$altabi_file = str_replace('.conf', '.altabi', $real_conf);
1512

    
1513
	$pkg_conf = array();
1514
	if (file_exists($abi_file) && file_exists($altabi_file)) {
1515
		$abi = file_get_contents($abi_file);
1516
		$altabi = file_get_contents($altabi_file);
1517

    
1518
		$pkg_conf = array(
1519
			"ABI={$abi}",
1520
			"ALTABI={$altabi}"
1521
		);
1522
	}
1523

    
1524
	$auth_ca = "/etc/ssl/netgate-ca.pem";
1525
	$auth_cert = "/etc/ssl/pfSense-repo-custom.cert";
1526
	$auth_key = "/etc/ssl/pfSense-repo-custom.key";
1527
	if (strpos($repo_path, "{$g['product_name']}-repo-custom") &&
1528
	    file_exists($auth_ca) && file_exists($auth_cert) &&
1529
	    file_exists($auth_key)) {
1530
		$pkg_conf[] = "PKG_ENV {\n";
1531
		$pkg_conf[] = "\tSSL_CA_CERT_FILE=$auth_ca\n";
1532
		$pkg_conf[] = "\tSSL_CLIENT_CERT_FILE=$auth_cert\n";
1533
		$pkg_conf[] = "\tSSL_CLIENT_KEY_FILE=$auth_key\n";
1534
		$pkg_conf[] = "}\n";
1535
	}
1536

    
1537
	file_put_contents($pkg_conf_path, $pkg_conf);
1538
}
1539

    
1540
/* Switch between stable and devel repos */
1541
function pkg_switch_repo($path) {
1542
	global $g;
1543

    
1544
	safe_mkdir("/usr/local/etc/pkg/repos");
1545
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1546
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1547

    
1548
	pkg_conf_setup($path);
1549

    
1550
	/* Update pfSense_version cache */
1551
	mwexec_bg("/etc/rc.update_pkg_metadata now");
1552
	return;
1553
}
1554

    
1555
$FQDN = "https://ews.netgate.com/pfupdate";
1556
$refreshinterval = (24 * 3600);	// 24 hours
1557
$idfile = "/var/db/uniqueid";
1558
$repopath = "/usr/local/share/{$g['product_name']}/pkg/repos";
1559
$configflename = "{$repopath}/{$g['product_name']}-repo-custom.conf";
1560
$cafilesrc = "/usr/local/share/{$g['product_name']}/ssl/netgate-ca.pem";
1561
$cafile = "/etc/ssl/netgate-ca.pem";
1562

    
1563
/*
1564
 * Update the list of available repositories from the server. This will allow
1565
 * migration to another update repository should the existing one become
1566
 * unavailable
1567
 */
1568
function update_repos() {
1569
	global $g, $cafile, $cafilesrc, $config, $idfile, $FQDN, $repopath;
1570

    
1571
	if (!file_exists($idfile) || !function_exists('curl_version')) {
1572
		return;
1573
	}
1574
	/* Copy the CA file to the proper place before start. */
1575
	if (!file_exists($cafile) && file_exists($cafilesrc)) {
1576
		copy($cafilesrc, $cafile);
1577
	}
1578
	/*
1579
	 * If the custom repository definition does not exist, or is more
1580
	 * than 24 hours old fetch a copy from the server
1581
	 */
1582
	if (!file_exists($configflename) ||
1583
	    (time()-filemtime($configflename) > $refreshinterval)) {
1584
		/*
1585
		 * Gather some information about the system so the proper
1586
		 * repo can be returned
1587
		 */
1588
		$nid = file_get_contents($idfile);
1589
		$serial = system_get_serial();
1590

    
1591
		// Compose a version string in JSON format
1592
		$os = php_uname('s');
1593
		$osver = php_uname('r');
1594
		$ed =  (strpos($g['product_label'], 'Plus') !== false)
1595
		    ? "Plus" : "Community";
1596
		$pkglist = get_pkg_info("all", false, true);
1597
		$platform = system_identify_specific_platform();
1598
		$platformname = gettext('Unknown system');
1599

    
1600
		if (isset($platform['descr'])) {
1601
			$platformname = $platform['descr'];
1602
		}
1603

    
1604
		$arch =  php_uname('m');
1605
		$locale = $config['system']['language'];
1606

    
1607
		// Find root file system type
1608
		$filesystems = get_mounted_filesystems();
1609
		$fstype = "";
1610

    
1611
		foreach ($filesystems as $fs)  {
1612
		   if ($fs['mountpoint'] == "/") {
1613
		      $fstype = strtoupper($fs['type']);
1614
		      break;
1615
		   }
1616
		}
1617

    
1618
		$va = array();
1619
		$va['platform'] = $platformname;
1620
		$va['os'] = $os;
1621
		$va['osver'] = $platformname;
1622
		$va['fstype'] = $fstype;
1623
		$va['prod'] = $g['product_label'];
1624
		$va['ver'] = $g['product_version_string'];
1625
		$va['ed'] = $ed;
1626
		$va['pkgs'] = array();
1627

    
1628
		foreach($pkglist as $pkg) {
1629
			$va['pkgs'][] = array('name' => $pkg['shortname'], 'ver' => $pkg['version']);
1630
		}
1631

    
1632
		$post = [
1633
		    'uid' => $nid,
1634
		    'language' => $locale,
1635
		    'serial' => $serial,
1636
		    'version' => json_encode($va),
1637
		    'arch' => $arch
1638
		];
1639

    
1640
		$ch = curl_init();
1641
		curl_setopt($ch, CURLOPT_HEADER, 0);
1642
		curl_setopt($ch, CURLOPT_VERBOSE, 0);
1643
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1644
		curl_setopt($ch, CURLOPT_USERAGENT, $g['product_label'] . '/' . $g['product_version']);
1645
		curl_setopt($ch, CURLOPT_URL, $FQDN);
1646
		curl_setopt($ch, CURLOPT_POST, true);
1647
		curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
1648
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,4);
1649
		set_curlproxy($ch);
1650

    
1651
		$response = curl_exec($ch);
1652
		$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1653

    
1654
		curl_close($ch);
1655

    
1656
		if ($status == 200) {
1657
			save_repo($response);
1658
		}
1659
	}
1660
}
1661

    
1662
// Parse the received JSON data and save the custom repository information
1663
function save_repo($json) {
1664
	global $repopath, $g, $config;
1665

    
1666
	$repo = json_decode($json, true);
1667
	$basename = "{$repopath}/{$g['product_name']}-repo-custom.";
1668

    
1669
	if (($repo != NULL) && isset($repo['abi']) && isset($repo['altabi']) &&
1670
	    isset($repo['conf']) && isset($repo['descr']) &&
1671
	    isset($repo['name']) && (strlen($repo['conf']) > 10)) {
1672

    
1673
		if (!empty($repo['conf']))		{file_put_contents($basename . "conf", base64_decode($repo['conf']));}
1674
		if (!empty($repo['descr']))		{file_put_contents($basename . "descr", $repo['descr']);}
1675
		if (!empty($repo['abi']))		{file_put_contents($basename . "abi", $repo['abi']);}
1676
		if (!empty($repo['altabi']))	{file_put_contents($basename . "altabi", $repo['altabi']);}
1677
		if (!empty($repo['name']))		{file_put_contents($basename . "name", $repo['name']);}
1678
		if (!empty($repo['help']))		{file_put_contents($basename . "help", $repo['help']);}
1679

    
1680

    
1681
		// Save fingerprint file
1682
		if (!empty($repo['fingerprint'])) {
1683
			if (!empty($repo['fpname'])) {
1684
				$fppath = "/usr/local/share/pfSense/keys/pkg/trusted/" . $repo['fpname'];
1685
			} else {
1686
				$fppath = "/usr/local/share/pfSense/keys/pkg/trusted/custom.pfsense.org";
1687
			}
1688

    
1689
			file_put_contents($fppath, $repo['fingerprint']);
1690
		}
1691

    
1692
		// Save the client Cert & key
1693
		if (!empty($repo['cert']) && !empty($repo['key'])) {
1694
			$certbasename = "/etc/ssl/{$g['product_name']}-repo-custom.";
1695
			file_put_contents($certbasename . "cert", $repo['cert']);
1696
			file_put_contents($certbasename . "key", $repo['key']);
1697
			chmod($certbasename . "key", 0600);
1698
		}
1699
	} else {
1700
		/*
1701
		 * If there was anything wrong with the custom repository
1702
		 * definition, remove the custom files to avoid possible confusion
1703
		 */
1704
		foreach (glob($basename . "*") as $f) {
1705
			unlink($f);
1706
		}
1707

    
1708
		// If config.xml still contains a reference to the custom repo, replace it
1709
		if ($config['system']['pkg_repo_conf_path'] == "/usr/local/share/pfSense/pkg/repos/pfSense-repo.conf") {
1710
			$config['system']['pkg_repo_conf_path'] = "/usr/local/share/pfSense/pkg/repos/pfSense-repo-next.conf";
1711
			write_config("-NoReMoTeBaCkUp");
1712
		}
1713
	}
1714
}
1715

    
1716
?>
(40-40/61)