Project

General

Profile

Download (42.1 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-2019 Rubicon Communications, LLC (Netgate)
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

    
23
require_once("globals.inc");
24
require_once("service-utils.inc");
25

    
26
if (file_exists("/cf/conf/use_xmlreader")) {
27
	require_once("xmlreader.inc");
28
} else {
29
	require_once("xmlparse.inc");
30
}
31

    
32
require_once("pfsense-utils.inc");
33

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

    
39
		if (!$debug) {
40
			return;
41
		}
42

    
43
		if (!$fd_log) {
44
			if (!$fd_log = fopen("{$g['tmp_path']}/pkg_mgr_debug.log", "w")) {
45
				update_status(gettext("Warning, could not open log for writing.") . "\n");
46
			}
47
		}
48
		@fwrite($fd_log, $msg);
49
	}
50
}
51

    
52
/* Validate if pkg name is valid */
53
function pkg_valid_name($pkgname) {
54
	global $g;
55

    
56
	$pattern = "/^{$g['pkg_prefix']}[a-zA-Z0-9\.\-_]+$/";
57
	return preg_match($pattern, $pkgname);
58
}
59

    
60
/* Remove pkg_prefix from package name if it's present */
61
function pkg_remove_prefix(&$pkg_name) {
62
	global $g;
63

    
64
	if (substr($pkg_name, 0, strlen($g['pkg_prefix'])) == $g['pkg_prefix']) {
65
		$pkg_name = substr($pkg_name, strlen($g['pkg_prefix']));
66
	}
67
}
68

    
69
/* Execute pkg update when it's necessary */
70
function pkg_update($force = false) {
71
	global $g;
72

    
73
	return pkg_call("update" . ($force ? " -f" : ""));
74
}
75

    
76
/* return an array with necessary environment vars for pkg */
77
function pkg_env($extra_env = array()) {
78
	global $config, $g;
79

    
80
	$user_agent = $g['product_name'] . '/' . $g['product_version'];
81
	if (!isset($config['system']['do_not_send_uniqueid'])) {
82
		$user_agent .= ':' . system_get_uniqueid();
83
	}
84

    
85
	$pkg_env_vars = array(
86
		"LANG" => "C",
87
		"HTTP_USER_AGENT" => $user_agent,
88
		"ASSUME_ALWAYS_YES" => "true",
89
		"FETCH_TIMEOUT" => 5,
90
		"FETCH_RETRY" => 2
91
	);
92

    
93
	if (!empty($config['system']['proxyurl'])) {
94
		$http_proxy = $config['system']['proxyurl'];
95
		if (!empty($config['system']['proxyport'])) {
96
			$http_proxy .= ':' . $config['system']['proxyport'];
97
		}
98
		$pkg_env_vars['HTTP_PROXY'] = $http_proxy;
99

    
100
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
101
			$pkg_env_vars['HTTP_PROXY_AUTH'] = "basic:*:{$config['system']['proxyuser']}:{$config['system']['proxypass']}";
102
		}
103
	}
104

    
105
	if (isset($config['system']['use_mfs_tmpvar'])) {
106
		$pkg_env_vars['PKG_DBDIR'] = '/root/var/db/pkg';
107
		$pkg_env_vars['PKG_CACHEDIR'] = '/root/var/cache/pkg';
108
	}
109

    
110
	foreach ($extra_env as $key => $value) {
111
		$pkg_env_vars[$key] = $value;
112
	}
113

    
114
	return $pkg_env_vars;
115
}
116

    
117
/* Execute a pkg call */
118
function pkg_call($params, $mute = false, $extra_env = array()) {
119
	global $g, $config;
120

    
121
	if (empty($params)) {
122
		return false;
123
	}
124

    
125
	$descriptorspec = array(
126
		1 => array("pipe", "w"), /* stdout */
127
		2 => array("pipe", "w")	 /* stderr */
128
	);
129

    
130
	pkg_debug("pkg_call(): {$params}\n");
131
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
132
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
133

    
134
	if (!is_resource($process)) {
135
		return false;
136
	}
137

    
138
	stream_set_blocking($pipes[1], 0);
139
	stream_set_blocking($pipes[2], 0);
140

    
141
	/* XXX: should be a tunnable? */
142
	$timeout = 60; // seconds
143
	$error_log = '';
144

    
145
	do {
146
		$write = array();
147
		$read = array($pipes[1], $pipes[2]);
148
		$except = array();
149

    
150
		$stream = @stream_select($read, $write, $except, $timeout);
151
		if ($stream !== FALSE && $stream > 0) {
152
			foreach ($read as $pipe) {
153
				$content = stream_get_contents($pipe);
154
				if ($content == '') {
155
					continue;
156
				}
157
				if ($pipe === $pipes[1]) {
158
					if (!$mute) {
159
						update_status($content);
160
					}
161
					flush();
162
				} else if ($pipe === $pipes[2]) {
163
					$error_log .= $content;
164
				}
165
			}
166
		}
167

    
168
		$status = proc_get_status($process);
169
	} while ($status['running']);
170

    
171
	fclose($pipes[1]);
172
	fclose($pipes[2]);
173
	proc_close($process);
174

    
175

    
176
	$rc = $status['exitcode'];
177

    
178
	pkg_debug("pkg_call(): rc = {$rc}\n");
179
	if ($rc == 0) {
180
		return true;
181
	}
182

    
183
	pkg_debug("pkg_call(): error_log\n{$error_log}\n");
184
	if (!$mute) {
185
		update_status("\n\n" .  sprintf(gettext(
186
		    "ERROR!!! An error occurred on pkg execution (rc = %d) with parameters '%s':"),
187
		    $rc, $params) . "\n" . $error_log . "\n");
188
	}
189

    
190
	return false;
191
}
192

    
193
/* Execute pkg with $params, fill stdout and stderr and return pkg rc */
194
function pkg_exec($params, &$stdout, &$stderr, $extra_env = array()) {
195
	global $g, $config;
196

    
197
	if (empty($params)) {
198
		return -1;
199
	}
200

    
201
	$descriptorspec = array(
202
		1 => array("pipe", "w"), /* stdout */
203
		2 => array("pipe", "w")	 /* stderr */
204
	);
205

    
206

    
207
	pkg_debug("pkg_exec(): {$params}\n");
208
	$process = proc_open("/usr/local/sbin/pkg-static {$params}",
209
	    $descriptorspec, $pipes, '/', pkg_env($extra_env));
210

    
211
	if (!is_resource($process)) {
212
		return -1;
213
	}
214

    
215
	$stdout = '';
216
	while (($l = fgets($pipes[1])) !== FALSE) {
217
		$stdout .= $l;
218
	}
219
	fclose($pipes[1]);
220

    
221
	$stderr = '';
222
	while (($l = fgets($pipes[2])) !== FALSE) {
223
		$stderr .= $l;
224
	}
225
	fclose($pipes[2]);
226

    
227

    
228
	return proc_close($process);
229
}
230

    
231
/* Compare 2 pkg versions and return:
232
 * '=' - versions are the same
233
 * '>' - $v1 > $v2
234
 * '<' - $v1 < $v2
235
 * '?' - Error
236
 */
237
function pkg_version_compare($v1, $v2) {
238
	if (empty($v1) || empty($v2)) {
239
		return '?';
240
	}
241

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

    
244
	if ($rc != 0) {
245
		return '?';
246
	}
247

    
248
	return str_replace("\n", "", $stdout);
249
}
250

    
251
/* Check if package is installed */
252
function is_pkg_installed($pkg_name) {
253
	global $g;
254

    
255
	if (empty($pkg_name)) {
256
		return false;
257
	}
258

    
259
	return pkg_call("info -e " . $pkg_name, true);
260
}
261

    
262
/* Install package, $pkg_name should not contain prefix */
263
function pkg_install($pkg_name, $force = false) {
264
	global $g;
265
	$result = false;
266

    
267
	$shortname = $pkg_name;
268
	pkg_remove_prefix($shortname);
269

    
270
	$pkg_force = "";
271
	if ($force) {
272
		$pkg_force = "-f ";
273
	}
274

    
275
	pkg_debug("Installing package {$shortname}\n");
276
	if ($force || !is_pkg_installed($pkg_name)) {
277
		$result = pkg_call("install -y " . $pkg_force . $pkg_name);
278
		/* Cleanup cacke to free disk space */
279
		pkg_call("clean -y");
280
	}
281

    
282
	return $result;
283
}
284

    
285
/* Delete package from FreeBSD, $pkg_name should not contain prefix */
286
function pkg_delete($pkg_name) {
287
	global $g;
288

    
289
	$shortname = $pkg_name;
290
	pkg_remove_prefix($shortname);
291

    
292
	pkg_debug("Removing package {$shortname}\n");
293
	if (is_pkg_installed($pkg_name)) {
294
		pkg_call("delete -y " . $pkg_name);
295
		/* Cleanup unecessary dependencies */
296
		pkg_call("autoremove -y");
297
	}
298
}
299

    
300
/* Check if package is present in config.xml */
301
function is_package_installed($package_name) {
302
	return (get_package_id($package_name) != -1);
303
}
304

    
305
/* Find package array index */
306
function get_package_id($package_name) {
307
	global $config;
308

    
309
	if (!isset($config['installedpackages']['package']) ||
310
	    !is_array($config['installedpackages']['package'])) {
311
		return -1;
312
	}
313

    
314
	foreach ($config['installedpackages']['package'] as $idx => $pkg) {
315
		if ($pkg['name'] == $package_name ||
316
		    get_package_internal_name($pkg) == $package_name) {
317
			return $idx;
318
		}
319
	}
320

    
321
	return -1;
322
}
323

    
324
/* Return internal_name when it's defined, otherwise, returns name */
325
function get_package_internal_name($package_data) {
326
	if (isset($package_data['internal_name']) && ($package_data['internal_name'] != "")) {
327
		/* e.g. name is Ipguard-dev, internal name is ipguard */
328
		return $package_data['internal_name'];
329
	} else {
330
		return $package_data['name'];
331
	}
332
}
333

    
334
// Get information about packages.
335
function get_pkg_info($pkgs = 'all', $remote_repo_usage_disabled = false,
336
    $installed_pkgs_only = false) {
337
	global $g, $input_errors;
338

    
339
	$out = $err = $extra_param = '';
340
	$rc = 0;
341

    
342
	unset($pkg_filter);
343

    
344
	if (is_array($pkgs)) {
345
		$pkg_filter = $pkgs;
346
		$pkgs = $g['pkg_prefix'] . '*';
347
	} elseif ($pkgs == 'all') {
348
		$pkgs = $g['pkg_prefix'] . '*';
349
	}
350

    
351
	$base_packages = (substr($pkgs, 0, strlen($g['pkg_prefix'])) !=
352
	    $g['pkg_prefix']);
353

    
354
	if ($installed_pkgs_only && !is_pkg_installed($pkgs)) {
355
		/*
356
		 * Return early if the caller wants just installed packages
357
		 * and there are none.  Saves doing any calls that might
358
		 * access a remote package repo.
359
		 */
360
		return array();
361
	}
362

    
363
	if (!function_exists('is_subsystem_dirty')) {
364
		require_once("util.inc");
365
	}
366

    
367
	/* Do not run remote operations if pkg has a lock */
368
	if (is_subsystem_dirty('pkg')) {
369
		$remote_repo_usage_disabled = true;
370
		$lock = false;
371
	} else {
372
		$lock = true;
373
	}
374

    
375
	if ($lock) {
376
		mark_subsystem_dirty('pkg');
377
	}
378

    
379
	if ($remote_repo_usage_disabled) {
380
		$extra_param = "-U ";
381
	}
382

    
383
	$did_search = false;
384
	$search_rc = 0;
385
	$info_rc = 0;
386
	$search_items = array();
387
	$info_items = array();
388

    
389
	if ($base_packages) {
390
		$repo_param = "";
391
	} else {
392
		$repo_param = "-r {$g['product_name']}";
393
	}
394

    
395
	/*
396
	 * If we want more than just the currently installed packages or
397
	 * we want up-to-date remote repo info then do a full pkg search
398
	 */
399
	if (!$installed_pkgs_only || !$remote_repo_usage_disabled) {
400
		$did_search = true;
401
		$search_rc = pkg_exec("search {$repo_param} " .
402
		    "{$extra_param}-R --raw-format json-compact " .
403
		    $pkgs, $search_out, $search_err);
404
		if ($search_rc == 0) {
405
			$search_items = explode("\n", chop($search_out));
406
			array_walk($search_items, function(&$v, &$k) {
407
				$v = json_decode($v, true);
408
			});
409
		}
410
	}
411

    
412
	/*
413
	 * We always should look for local items to detect packages that
414
	 * were removed from remote repo but are already installed locally
415
	 *
416
	 * Take pkg search return code into consideration to fallback to local
417
	 * information when remote repo is not accessible
418
	 */
419
	if (is_pkg_installed($pkgs) || $search_rc != 0) {
420
		$info_rc = pkg_exec("info -R --raw-format json-compact " .
421
		    $pkgs, $info_out, $info_err);
422
		if ($info_rc == 0) {
423
			$info_items = explode("\n", chop($info_out));
424
			array_walk($info_items, function(&$v, &$k) {
425
				$v = json_decode($v, true);
426
			});
427
		}
428
	}
429

    
430
	if ($lock) {
431
		clear_subsystem_dirty('pkg');
432
	}
433

    
434
	if ($search_rc != 0 && $info_rc != 0) {
435
		update_status("\n" . gettext(
436
		    "ERROR: Error trying to get packages list. Aborting...")
437
		    . "\n");
438
		update_status($search_err . "\n" . $info_err);
439
		$input_errors[] = gettext(
440
		    "ERROR: Error trying to get packages list. Aborting...") .
441
		    "\n";
442
		$input_errors[] = $search_err . "\n" . $info_err;
443
		return array();
444
	}
445

    
446
	/* It was not possible to search, use local information only */
447
	if ($search_rc != 0 || !$did_search) {
448
		$search_items = $info_items;
449
	} else {
450
		foreach ($info_items as $pkg_info) {
451
			if (empty($pkg_info['name'])) {
452
				continue;
453
			}
454

    
455
			if (array_search($pkg_info['name'], array_column(
456
			    $search_items, 'name')) === FALSE) {
457
				$pkg_info['obsolete'] = true;
458
				$search_items[] = $pkg_info;
459
			}
460
		}
461
	}
462

    
463
	$result = array();
464
	foreach ($search_items as $pkg_info) {
465
		if (empty($pkg_info['name'])) {
466
			continue;
467
		}
468

    
469
		if (isset($pkg_filter) && !in_array($pkg_info['name'],
470
		    $pkg_filter)) {
471
			continue;
472
		}
473

    
474
		$pkg_info['shortname'] = $pkg_info['name'];
475
		pkg_remove_prefix($pkg_info['shortname']);
476

    
477
		/* XXX: Add it to globals.inc? */
478
		$pkg_info['changeloglink'] =
479
		    "https://github.com/pfsense/FreeBSD-ports/commits/devel/" .
480
		    $pkg_info['categories'][0] . '/' . $pkg_info['name'];
481

    
482
		$pkg_is_installed = false;
483

    
484
		if (is_pkg_installed($pkg_info['name'])) {
485
			$rc = pkg_exec("query %R {$pkg_info['name']}", $out,
486
			    $err);
487
			if (!$base_packages &&
488
			    rtrim($out) != $g['product_name']) {
489
				continue;
490
			}
491

    
492
			$pkg_info['installed'] = true;
493
			$pkg_is_installed = true;
494

    
495
			$rc = pkg_exec("query %v {$pkg_info['name']}", $out,
496
			    $err);
497

    
498
			if ($rc != 0) {
499
				update_status("\n" . gettext("ERROR: Error " .
500
				    "trying to get package version. " .
501
				    "Aborting...") . "\n");
502
				update_status($err);
503
				$input_errors[] = gettext("ERROR: Error " .
504
				    "trying to get package version. " .
505
				    "Aborting...") . "\n";
506
				$input_errors[] = $err;
507
				return array();
508
			}
509

    
510
			$pkg_info['installed_version'] = str_replace("\n", "",
511
			    $out);
512

    
513
			/*
514
			 * We used pkg info to collect pkg data so remote
515
			 * version is not available. Lets try to collect it
516
			 * using rquery if possible
517
			 */
518
			if ($search_rc != 0 || !$did_search) {
519
				$rc = pkg_exec(
520
				    "rquery -U %v {$pkg_info['name']}", $out,
521
				    $err);
522

    
523
				if ($rc == 0) {
524
					$pkg_info['version'] =
525
					    str_replace("\n", "", $out);
526
				}
527
			}
528

    
529
		} else if (is_package_installed($pkg_info['shortname'])) {
530
			$pkg_info['broken'] = true;
531
			$pkg_is_installed = true;
532
		}
533

    
534
		$pkg_info['desc'] = preg_replace('/\n+WWW:.*$/', '',
535
		    $pkg_info['desc']);
536

    
537
		if (!$installed_pkgs_only || $pkg_is_installed) {
538
			$result[] = $pkg_info;
539
		}
540
		unset($pkg_info);
541
	}
542

    
543
	/* Sort result alphabetically */
544
	usort($result, function($a, $b) {
545
		return(strcasecmp ($a['name'], $b['name']));
546
	});
547

    
548
	return $result;
549
}
550

    
551
/*
552
 * If binary pkg is installed but post-install tasks were not
553
 * executed yet, do it now.
554
 * This scenario can happen when a pkg is pre-installed during
555
 * build phase, and at this point, cannot find a running system
556
 * to register itself in config.xml and also execute custom
557
 * install functions
558
 */
559
function register_all_installed_packages() {
560
	global $g, $config, $pkg_interface;
561

    
562
	$pkg_info = get_pkg_info('all', true, true);
563

    
564
	foreach ($pkg_info as $pkg) {
565
		pkg_remove_prefix($pkg['name']);
566

    
567
		if (is_package_installed($pkg['name'])) {
568
			continue;
569
		}
570

    
571
		update_status(sprintf(gettext(
572
		    "Running last steps of %s installation.") . "\n",
573
		    $pkg['name']));
574
		install_package_xml($pkg['name']);
575
	}
576
}
577

    
578
/*
579
 * resync_all_package_configs() Force packages to setup their configuration and rc.d files.
580
 * This function may also print output to the terminal indicating progress.
581
 */
582
function resync_all_package_configs($show_message = false) {
583
	global $config, $pkg_interface, $g;
584

    
585
	log_error(gettext("Resyncing configuration for all packages."));
586

    
587
	if (!isset($config['installedpackages']['package']) ||
588
	    !is_array($config['installedpackages']['package'])) {
589
		return;
590
	}
591

    
592
	if ($show_message == true) {
593
		echo "Syncing packages:";
594
	}
595

    
596

    
597
	foreach ($config['installedpackages']['package'] as $idx => $package) {
598
		if (empty($package['name'])) {
599
			continue;
600
		}
601
		if ($show_message == true) {
602
			echo " " . $package['name'];
603
		}
604
		if (platform_booting() != true) {
605
			stop_service(get_package_internal_name($package));
606
		}
607
		sync_package($package['name']);
608
		update_status(gettext("Syncing packages...") . "\n");
609
	}
610

    
611
	if ($show_message == true) {
612
		echo " done.\n";
613
	}
614
}
615

    
616
function uninstall_package($package_name) {
617
	global $config;
618

    
619
	$internal_name = $package_name;
620
	$id = get_package_id($package_name);
621
	if ($id >= 0) {
622
		$internal_name = get_package_internal_name($config['installedpackages']['package'][$id]);
623
		stop_service($internal_name);
624
	}
625
	$pkg_name = $g['pkg_prefix'] . $internal_name;
626

    
627
	if (is_pkg_installed($pkg_name)) {
628
		update_status(gettext("Removing package...") . "\n");
629
		pkg_delete($pkg_name);
630
	} else {
631
		delete_package_xml($package_name);
632
	}
633

    
634
	update_status(gettext("done.") . "\n");
635
}
636

    
637
function reinstall_package($package_name) {
638
	global $config, $g;
639

    
640
	$internal_name = $package_name;
641
	$id = get_package_id($package_name);
642
	if ($id >= 0) {
643
		$internal_name = get_package_internal_name($config['installedpackages']['package'][$id]);
644
	}
645
	$pkg_name = $g['pkg_prefix'] . $internal_name;
646
	pkg_install($pkg_name);
647
}
648

    
649
/* Run <custom_php_resync_config_command> */
650
function sync_package($package_name) {
651
	global $config, $builder_package_install;
652

    
653
	// If this code is being called by pfspkg_installer
654
	// which the builder system uses then return (ignore).
655
	if ($builder_package_install) {
656
		return;
657
	}
658

    
659
	if (empty($config['installedpackages']['package'])) {
660
		return;
661
	}
662

    
663
	if (($pkg_id = get_package_id($package_name)) == -1) {
664
		return; // This package doesn't really exist - exit the function.
665
	}
666

    
667
	if (!is_array($config['installedpackages']['package'][$pkg_id])) {
668
		return;	 // No package belongs to the pkg_id passed to this function.
669
	}
670

    
671
	$package = &$config['installedpackages']['package'][$pkg_id];
672
	if (!file_exists("/usr/local/pkg/" . $package['configurationfile'])) {
673
		log_error(sprintf(gettext("The %s package is missing its configuration file and must be reinstalled."), $package['name']));
674
		delete_package_xml($package['name']);
675
		return;
676
	}
677

    
678
	$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $package['configurationfile'], "packagegui");
679
	if (isset($pkg_config['nosync'])) {
680
		return;
681
	}
682

    
683
	/* Bring in package include files */
684
	if (!empty($pkg_config['include_file'])) {
685
		$include_file = $pkg_config['include_file'];
686
		if (file_exists($include_file)) {
687
			require_once($include_file);
688
		} else {
689
			log_error(sprintf(gettext('Reinstalling package %1$s because its include file(%2$s) is missing!'), $package['name'], $include_file));
690
			uninstall_package($package['name']);
691
			if (reinstall_package($package['name']) != 0) {
692
				log_error(sprintf(gettext("Reinstalling package %s failed. Take appropriate measures!!!"), $package['name']));
693
				return;
694
			}
695
			if (file_exists($include_file)) {
696
				require_once($include_file);
697
			} else {
698
				return;
699
			}
700
		}
701
	}
702

    
703
	if (!empty($pkg_config['custom_php_global_functions'])) {
704
		eval($pkg_config['custom_php_global_functions']);
705
	}
706
	if (!empty($pkg_config['custom_php_resync_config_command'])) {
707
		eval($pkg_config['custom_php_resync_config_command']);
708
	}
709
}
710

    
711
/* Read info.xml installed by package and return an array */
712
function read_package_config($package_name) {
713
	global $g;
714

    
715
	$pkg_info_xml = '/usr/local/share/' . $g['pkg_prefix'] . $package_name . '/info.xml';
716

    
717
	if (!file_exists($pkg_info_xml)) {
718
		return false;
719
	}
720

    
721
	$pkg_info = parse_xml_config_pkg($pkg_info_xml, 'pfsensepkgs');
722

    
723
	if (empty($pkg_info)) {
724
		return false;
725
	}
726

    
727
	/* it always returns an array with 1 item */
728
	return $pkg_info['package'][0];
729
}
730

    
731
/* Read package configurationfile and return an array */
732
function read_package_configurationfile($package_name) {
733
	global $config, $g;
734

    
735
	$pkg_config = array();
736
	$id = get_package_id($package_name);
737

    
738
	if ($id < 0 || !isset($config['installedpackages']['package'][$id]['configurationfile'])) {
739
		return $pkg_config;
740
	}
741

    
742
	$pkg_configurationfile = $config['installedpackages']['package'][$id]['configurationfile'];
743

    
744
	if (empty($pkg_configurationfile) || !file_exists('/usr/local/pkg/' . $pkg_configurationfile)) {
745
		return $pkg_config;
746
	}
747

    
748
	$pkg_config = parse_xml_config_pkg('/usr/local/pkg/' . $pkg_configurationfile, "packagegui");
749

    
750
	return $pkg_config;
751
}
752

    
753
function get_after_install_info($package_name) {
754
	$pkg_config = read_package_config($package_name);
755

    
756
	if (isset($pkg_config['after_install_info'])) {
757
		return $pkg_config['after_install_info'];
758
	}
759

    
760
	return '';
761
}
762

    
763
function eval_once($toeval) {
764
	global $evaled;
765
	if (!$evaled) {
766
		$evaled = array();
767
	}
768
	$evalmd5 = md5($toeval);
769
	if (!in_array($evalmd5, $evaled)) {
770
		@eval($toeval);
771
		$evaled[] = $evalmd5;
772
	}
773
	return;
774
}
775

    
776
function install_package_xml($package_name) {
777
	global $g, $config, $pkg_interface;
778

    
779
	if (($pkg_info = read_package_config($package_name)) == false) {
780
		return false;
781
	}
782

    
783
	/* safe side. Write config below will send to ro again. */
784

    
785
	pkg_debug(gettext("Beginning package installation.") . "\n");
786
	log_error(sprintf(gettext('Beginning package installation for %s .'), $pkg_info['name']));
787

    
788
	/* add package information to config.xml */
789
	$pkgid = get_package_id($pkg_info['name']);
790
	update_status(gettext("Saving updated package information...") . "\n");
791
	if ($pkgid == -1) {
792
		init_config_arr(array('installedpackages', 'package'));
793
		$config['installedpackages']['package'][] = $pkg_info;
794
		$changedesc = sprintf(gettext("Installed %s package."), $pkg_info['name']);
795
		$to_output = gettext("done.") . "\n";
796
	} else {
797
		init_config_arr(array('installedpackages', 'package', $pkgid));
798
		$config['installedpackages']['package'][$pkgid] = $pkg_info;
799
		$changedesc = sprintf(gettext("Overwrote previous installation of %s."), $pkg_info['name']);
800
		$to_output = gettext("overwrite!") . "\n";
801
	}
802
	write_config(sprintf(gettext("Intermediate config write during package install for %s."), $pkg_info['name']));
803
	update_status($to_output);
804

    
805
	if (($pkgid = get_package_id($package_name)) == -1) {
806
		update_status(sprintf(gettext('The %1$s package is not installed.%2$sInstallation aborted.'), $package_name, "\n\n"));
807

    
808
		uninstall_package($package_name);
809
		write_config($changedesc);
810
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
811
		update_status(gettext("Failed to install package.") . "\n");
812
		return false;
813
	}
814

    
815
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
816
		update_status(gettext("Loading package configuration... "));
817
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $pkg_info['configurationfile'], "packagegui");
818
		update_status(gettext("done.") . "\n");
819
		update_status(gettext("Configuring package components...") . "\n");
820
		if (!empty($pkg_config['filter_rules_needed'])) {
821
			$config['installedpackages']['package'][$pkgid]['filter_rule_function'] = $pkg_config['filter_rules_needed'];
822
		}
823
		/* modify system files */
824

    
825
		/* if a require exists, include it.  this will
826
		 * show us where an error exists in a package
827
		 * instead of making us blindly guess
828
		 */
829
		$missing_include = false;
830
		if ($pkg_config['include_file'] <> "") {
831
			update_status(gettext("Loading package instructions...") . "\n");
832
			if (file_exists($pkg_config['include_file'])) {
833
				pkg_debug("require_once('{$pkg_config['include_file']}')\n");
834
				require_once($pkg_config['include_file']);
835
			} else {
836
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
837
				$missing_include = true;
838
				update_status(sprintf(gettext("Include %s is missing!"), basename($pkg_config['include_file'])) . "\n");
839

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

    
848
		/* custom commands */
849
		update_status(gettext("Custom commands...") . "\n");
850
		if ($missing_include == false) {
851
			if ($pkg_config['custom_php_global_functions'] <> "") {
852
				update_status(gettext("Executing custom_php_global_functions()..."));
853
				eval_once($pkg_config['custom_php_global_functions']);
854
				update_status(gettext("done.") . "\n");
855
			}
856
			if ($pkg_config['custom_php_install_command']) {
857
				update_status(gettext("Executing custom_php_install_command()..."));
858
				eval_once($pkg_config['custom_php_install_command']);
859
				update_status(gettext("done.") . "\n");
860
			}
861
			if ($pkg_config['custom_php_resync_config_command'] <> "") {
862
				update_status(gettext("Executing custom_php_resync_config_command()..."));
863
				eval_once($pkg_config['custom_php_resync_config_command']);
864
				update_status(gettext("done.") . "\n");
865
			}
866
		}
867
		/* sidebar items */
868
		if (is_array($pkg_config['menu'])) {
869
			update_status(gettext("Menu items... "));
870
			foreach ($pkg_config['menu'] as $menu) {
871
				if (is_array($config['installedpackages']['menu'])) {
872
					foreach ($config['installedpackages']['menu'] as $amenu) {
873
						if ($amenu['name'] == $menu['name']) {
874
							continue 2;
875
						}
876
					}
877
				} else {
878
					$config['installedpackages']['menu'] = array();
879
				}
880
				$config['installedpackages']['menu'][] = $menu;
881
			}
882
			update_status(gettext("done.") . "\n");
883
		}
884
		/* services */
885
		if (is_array($pkg_config['service'])) {
886
			update_status(gettext("Services... "));
887
			foreach ($pkg_config['service'] as $service) {
888
				if (is_array($config['installedpackages']['service'])) {
889
					foreach ($config['installedpackages']['service'] as $aservice) {
890
						if ($aservice['name'] == $service['name']) {
891
							continue 2;
892
						}
893
					}
894
				} else {
895
					$config['installedpackages']['service'] = array();
896
				}
897
				$config['installedpackages']['service'][] = $service;
898
			}
899
			update_status(gettext("done.") . "\n");
900
		}
901
 		if (is_array($pkg_config['tabs'])) {
902
 			$config['installedpackages']['package'][$pkgid]['tabs'] = $pkg_config['tabs'];
903
 		}
904
		/* plugins */
905
		if (isset($pkg_config['include_file'])) {
906
			$config['installedpackages']['package'][$pkgid]['include_file'] = $pkg_config['include_file'];
907
		}
908
		if (is_array($pkg_config['plugins']['item'])) {
909
			$config['installedpackages']['package'][$pkgid]['plugins']['item'] = $pkg_config['plugins']['item'];
910
		}
911
	} else {
912
		pkg_debug("Unable to find config file\n");
913
		update_status(gettext("Loading package configuration... failed!") . "\n\n" . gettext("Installation aborted."));
914
		pkg_debug(gettext("Unable to load package configuration. Installation aborted.") ."\n");
915

    
916
		uninstall_package($package_name);
917
		write_config($changedesc);
918
		log_error(sprintf(gettext("Failed to install package: %s."), $pkg_info['name']));
919
		update_status(gettext("Failed to install package.") . "\n");
920
		return false;
921
	}
922

    
923
	update_status(gettext("Writing configuration... "));
924
	write_config($changedesc);
925
	log_error(sprintf(gettext("Successfully installed package: %s."), $pkg_info['name']));
926
	update_status(gettext("done.") . "\n");
927
	if ($pkg_info['after_install_info']) {
928
		update_status($pkg_info['after_install_info']);
929
	}
930

    
931
	/* set up package logging streams */
932
	if ($pkg_info['logging']) {
933
		system_syslogd_start(true);
934
	}
935

    
936
	return true;
937
}
938

    
939
function delete_package_xml($package_name, $when = "post-deinstall") {
940
	global $g, $config, $pkg_interface;
941

    
942

    
943
	$pkgid = get_package_id($package_name);
944
	if ($pkgid == -1) {
945
		update_status(sprintf(gettext('The %1$s package is not installed.%2$sDeletion aborted.'), $package_name, "\n\n"));
946
		ob_flush();
947
		sleep(1);
948
		return;
949
	}
950
	pkg_debug(sprintf(gettext("Removing %s package... "), $package_name));
951
	update_status(sprintf(gettext("Removing %s components..."), $package_name) . "\n");
952
	/* parse package configuration */
953
	init_config_arr(array('installedpackages', 'package', $pkgid));
954
	$packages = &$config['installedpackages']['package'];
955
	$pkg_info = &$packages[$pkgid];
956
	init_config_arr(array('installedpackages', 'menu'));
957
	$menus = &$config['installedpackages']['menu'];
958
	init_config_arr(array('installedpackages', 'service'));
959
	$services = &$config['installedpackages']['service'];
960
	if (file_exists("/usr/local/pkg/" . $pkg_info['configurationfile'])) {
961
		$pkg_config = parse_xml_config_pkg("/usr/local/pkg/" . $packages[$pkgid]['configurationfile'], "packagegui");
962
		/* remove menu items */
963
		if (is_array($pkg_config['menu'])) {
964
			update_status(gettext("Menu items... "));
965
			if (is_array($pkg_config['menu']) && is_array($menus)) {
966
				foreach ($pkg_config['menu'] as $menu) {
967
					foreach ($menus as $key => $instmenu) {
968
						if ($instmenu['name'] == $menu['name']) {
969
							unset($menus[$key]);
970
							break;
971
						}
972
					}
973
				}
974
			}
975
			update_status(gettext("done.") . "\n");
976
		}
977
		/* remove services */
978
		if (is_array($pkg_config['service'])) {
979
			update_status(gettext("Services... "));
980
			if (is_array($pkg_config['service']) && is_array($services)) {
981
				foreach ($pkg_config['service'] as $service) {
982
					foreach ($services as $key => $instservice) {
983
						if ($instservice['name'] == $service['name']) {
984
							if (platform_booting() != true) {
985
								stop_service($service['name']);
986
							}
987
							if ($service['rcfile']) {
988
								$prefix = RCFILEPREFIX;
989
								if (!empty($service['prefix'])) {
990
									$prefix = $service['prefix'];
991
								}
992
								if (file_exists("{$prefix}{$service['rcfile']}")) {
993
									@unlink("{$prefix}{$service['rcfile']}");
994
								}
995
							}
996
							unset($services[$key]);
997
						}
998
					}
999
				}
1000
			}
1001
			update_status(gettext("done.") . "\n");
1002
		}
1003
		/*
1004
		 * XXX: Otherwise inclusion of config.inc again invalidates actions taken.
1005
		 *	Same is done during installation.
1006
		 */
1007
		write_config(sprintf(gettext("Intermediate config write during package removal for %s."), $package_name));
1008

    
1009
		/*
1010
		 * If a require exists, include it.	 this will
1011
		 * show us where an error exists in a package
1012
		 * instead of making us blindly guess
1013
		 */
1014
		$missing_include = false;
1015
		if ($pkg_config['include_file'] <> "") {
1016
			update_status(gettext("Loading package instructions...") . "\n");
1017
			if (file_exists($pkg_config['include_file'])) {
1018
				pkg_debug("require_once(\"{$pkg_config['include_file']}\")\n");
1019
				require_once($pkg_config['include_file']);
1020
			} else {
1021
				pkg_debug("Missing include {$pkg_config['include_file']}\n");
1022
				$missing_include = true;
1023
				update_status(sprintf(gettext("Include file %s could not be found for inclusion."), basename($pkg_config['include_file'])) . "\n");
1024
			}
1025
		}
1026
		/* ermal
1027
		 * NOTE: It is not possible to handle parse errors on eval.
1028
		 * So we prevent it from being run at all to not interrupt all the other code.
1029
		 */
1030
		if ($when == "deinstall" && $missing_include == false) {
1031
			/* evaluate this package's global functions and pre deinstall commands */
1032
			if ($pkg_config['custom_php_global_functions'] <> "") {
1033
				eval_once($pkg_config['custom_php_global_functions']);
1034
			}
1035
			if ($pkg_config['custom_php_pre_deinstall_command'] <> "") {
1036
				eval_once($pkg_config['custom_php_pre_deinstall_command']);
1037
			}
1038
		}
1039
		/* deinstall commands */
1040
		if ($when == "deinstall" && $pkg_config['custom_php_deinstall_command'] <> "") {
1041
			update_status(gettext("Deinstall commands... "));
1042
			if ($missing_include == false) {
1043
				eval_once($pkg_config['custom_php_deinstall_command']);
1044
				update_status(gettext("done.") . "\n");
1045
			} else {
1046
				update_status("\n". gettext("Not executing custom deinstall hook because an include is missing.") . "\n");
1047
			}
1048
		}
1049
	}
1050
	/* syslog */
1051
	$need_syslog_restart = false;
1052
	if (is_array($pkg_info['logging']) && $pkg_info['logging']['logfilename'] <> "") {
1053
		update_status(gettext("Syslog entries... "));
1054
		@unlink_if_exists("{$g['varlog_path']}/{$pkg_info['logging']['logfilename']}");
1055
		update_status("done.\n");
1056
		$need_syslog_restart = true;
1057
	}
1058

    
1059
	if ($when == "post-deinstall") {
1060
		/* remove config.xml entries */
1061
		update_status(gettext("Configuration... "));
1062
		unset($config['installedpackages']['package'][$pkgid]);
1063
		update_status(gettext("done.") . "\n");
1064
		write_config(sprintf(gettext("Removed %s package."), $package_name));
1065
		/* remove package entry from /etc/syslog.conf if needed */
1066
		/* this must be done after removing the entries from config.xml */
1067
		if ($need_syslog_restart) {
1068
			system_syslogd_start(true);
1069
		}
1070
	}
1071
}
1072

    
1073
/*
1074
 * Used during upgrade process or retore backup process, verify all
1075
 * packages installed in config.xml and install pkg accordingly
1076
 */
1077
function package_reinstall_all() {
1078
	global $g, $config, $pkg_interface;
1079

    
1080
	if (!isset($config['installedpackages']['package']) ||
1081
	    !is_array($config['installedpackages']['package'])) {
1082
		return true;
1083
	}
1084

    
1085
	/*
1086
	 * Configure default pkg repo for current version instead of
1087
	 * using it from backup, that could be older
1088
	 */
1089
	$default_repo = pkg_get_default_repo();
1090
	$current_repo_path = "";
1091
	if (!empty($config['system']['pkg_repo_conf_path'])) {
1092
		$current_repo_path = $config['system']['pkg_repo_conf_path'];
1093
	}
1094

    
1095
	if ($current_repo_path != $default_repo['path']) {
1096
		$config['system']['pkg_repo_conf_path'] = $default_repo['path'];
1097
		write_config( "Configured default pkg repo after restore");
1098
		pkg_switch_repo($default_repo['path']);
1099
	}
1100

    
1101
	/* wait for internet connection */
1102
	log_error(gettext("Waiting for Internet connection to update pkg " .
1103
	    "metadata and finish package reinstallation"));
1104
	$ntries = 3;
1105
	while ($ntries > 0) {
1106
		if (pkg_update(true)) {
1107
			break;
1108
		}
1109
		sleep(1);
1110
		$ntries--;
1111
	}
1112

    
1113
	if ($ntries == 0) {
1114
		return false;
1115
	}
1116

    
1117
	$package_list = array();
1118
	foreach ($config['installedpackages']['package'] as $package) {
1119
		$package_list[] = get_package_internal_name($package);
1120
	}
1121

    
1122
	if (!empty($package_list)) {
1123
		$pkg_info = get_pkg_info();
1124
	}
1125

    
1126
	foreach ($package_list as $package) {
1127
		$found = false;
1128
		foreach ($pkg_info as $pkg) {
1129
			pkg_remove_prefix($pkg['name']);
1130
			if ($pkg['name'] == $package) {
1131
				pkg_install($g['pkg_prefix'] . $package, true);
1132
				$found = true;
1133
				break;
1134
			}
1135
		}
1136

    
1137
		if (!$found) {
1138
			if (!function_exists("file_notice")) {
1139
				require_once("notices.inc");
1140
			}
1141

    
1142
			file_notice(gettext("Package reinstall"),
1143
			    sprintf(gettext("Package %s does not exist in " .
1144
			    "current %s version and it has been removed."),
1145
			    $package, $g['product_name']));
1146
			uninstall_package($package);
1147
		}
1148
	}
1149

    
1150
	/*
1151
	 * Verify remaining binary packages not present in current config
1152
	 * during backup restore and remove them
1153
	 */
1154
	$installed_packages = get_pkg_info('all', true, true);
1155
	foreach ($installed_packages as $package) {
1156
		$shortname = $package['name'];
1157
		pkg_remove_prefix($shortname);
1158
		if (get_package_id($shortname) != -1) {
1159
			continue;
1160
		}
1161
		pkg_delete($package['name']);
1162
	}
1163

    
1164
	return true;
1165
}
1166

    
1167
function stop_packages() {
1168
	require_once("config.inc");
1169
	require_once("functions.inc");
1170
	require_once("filter.inc");
1171
	require_once("shaper.inc");
1172
	require_once("captiveportal.inc");
1173
	require_once("pkg-utils.inc");
1174
	require_once("pfsense-utils.inc");
1175
	require_once("service-utils.inc");
1176

    
1177
	global $config, $g;
1178

    
1179
	log_error(gettext("Stopping all packages."));
1180

    
1181
	$rcfiles = glob(RCFILEPREFIX . "*.sh");
1182
	if (!$rcfiles) {
1183
		$rcfiles = array();
1184
	} else {
1185
		$rcfiles = array_flip($rcfiles);
1186
		if (!$rcfiles) {
1187
			$rcfiles = array();
1188
		}
1189
	}
1190

    
1191
	if (isset($config['installedpackages']['package']) &&
1192
	    is_array($config['installedpackages']['package'])) {
1193
		foreach ($config['installedpackages']['package'] as $package) {
1194
			echo " Stopping package {$package['name']}...";
1195
			$internal_name = get_package_internal_name($package);
1196
			stop_service($internal_name);
1197
			unset($rcfiles[RCFILEPREFIX . strtolower($internal_name) . ".sh"]);
1198
			echo "done.\n";
1199
		}
1200
	}
1201

    
1202
	foreach ($rcfiles as $rcfile => $number) {
1203
		$shell = @popen("/bin/sh", "w");
1204
		if ($shell) {
1205
			echo " Stopping {$rcfile}...";
1206
			if (!@fwrite($shell, "{$rcfile} stop >>/tmp/bootup_messages 2>&1")) {
1207
				if ($shell) {
1208
					pclose($shell);
1209
				}
1210
				$shell = @popen("/bin/sh", "w");
1211
			}
1212
			echo "done.\n";
1213
			pclose($shell);
1214
		}
1215
	}
1216
}
1217

    
1218
/* Identify which meta package is installed */
1219
function get_meta_pkg_name() {
1220
	global $g;
1221

    
1222
	/* XXX: Use pkg annotation */
1223
	if (is_pkg_installed($g['product_name'])) {
1224
		return $g['product_name'];
1225
	}
1226
	foreach ($g['alternativemetaports'] as $suffix) {
1227
		if (is_pkg_installed($g['product_name'] . '-' . $suffix)) {
1228
			return $g['product_name'] . '-' . $suffix;
1229
		}
1230
	}
1231
	return false;
1232
}
1233

    
1234
/* Identify which base package is installed */
1235
function get_base_pkg_name() {
1236
	global $g;
1237

    
1238
	/* XXX: Use pkg annotation */
1239
	if (is_pkg_installed($g['product_name'] . '-base-' . $g['platform'])) {
1240
		return $g['product_name'] . '-base-' . $g['platform'];
1241
	} else if (is_pkg_installed($g['product_name'] . '-base')) {
1242
		return $g['product_name'] . '-base';
1243
	}
1244
	return false;
1245
}
1246

    
1247
/* Verify if system needs upgrade (meta package or base) */
1248
function get_system_pkg_version($baseonly = false, $use_cache = true) {
1249
	global $g;
1250

    
1251
	$cache_file = $g['version_cache_file'];
1252
	$rc_file = $cache_file . '.rc';
1253

    
1254
	$rc = "";
1255
	if ($use_cache && file_exists($rc_file) &&
1256
	    (time()-filemtime($rc_file) < $g['version_cache_refresh'])) {
1257
		$rc = chop(@file_get_contents($rc_file));
1258
	}
1259

    
1260
	if ($rc == "2") {
1261
		$output = @file_get_contents($cache_file);
1262
	} else if ($rc != "0") {
1263
		$output = exec(
1264
		    "/usr/local/sbin/{$g['product_name']}-upgrade -c", $_gc,
1265
		    $rc);
1266

    
1267
		/* Update cache if it succeeded */
1268
		if ($rc == 0 || $rc == 2) {
1269
			@file_put_contents($cache_file, $output);
1270
			@file_put_contents($rc_file, $rc);
1271
		}
1272
	}
1273

    
1274
	/* pfSense-upgrade returns 2 when there is a new version */
1275
	if ($rc == "2") {
1276
		$new_version = explode(' ', $output)[0];
1277
	}
1278

    
1279
	$base_pkg = get_base_pkg_name();
1280
	$meta_pkg = get_meta_pkg_name();
1281

    
1282
	if (!$base_pkg || !$meta_pkg) {
1283
		return false;
1284
	}
1285

    
1286
	$info = get_pkg_info($base_pkg, true, true);
1287

    
1288
	$pkg_info = array();
1289
	foreach ($info as $item) {
1290
		if ($item['name'] == $base_pkg) {
1291
			$pkg_info = $item;
1292
			break;
1293
		}
1294
	}
1295

    
1296
	if (empty($pkg_info) || (!$baseonly && ($pkg_info['version'] ==
1297
	    $pkg_info['installed_version']))) {
1298
		$info = get_pkg_info($meta_pkg, true, true);
1299

    
1300
		foreach ($info as $item) {
1301
			if ($item['name'] == $meta_pkg) {
1302
				$pkg_info = $item;
1303
				break;
1304
			}
1305
		}
1306
	}
1307

    
1308
	if (empty($pkg_info)) {
1309
		return false;
1310
	}
1311

    
1312
	$result = array(
1313
	    'version'           => $new_version ?: $pkg_info['version'],
1314
	    'installed_version' => $pkg_info['installed_version']
1315
	);
1316

    
1317
	$result['pkg_version_compare'] = pkg_version_compare(
1318
	    $result['installed_version'], $result['version']);
1319

    
1320
	return $result;
1321
}
1322

    
1323
/* List available repos */
1324
function pkg_list_repos() {
1325
	global $g;
1326

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

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

    
1331
	$default = array(
1332
	    'name' => 'Default',
1333
	    'path' => $path . "/{$g['product_name']}-repo.conf",
1334
	    'descr' => $default_descr
1335
	);
1336

    
1337
	$result = array($default);
1338

    
1339
	$conf_files = glob("{$path}/{$g['product_name']}-repo-*.conf");
1340
	foreach ($conf_files as $conf_file) {
1341
		$descr_file = preg_replace('/.conf$/', '.descr', $conf_file);
1342
		if (file_exists($descr_file)) {
1343
			$descr_content = file($descr_file);
1344
			$descr = chop($descr_content[0]);
1345
		} else {
1346
			$descr = 'Unknown';
1347
		}
1348
		if (!preg_match('/-repo-(.*).conf/', $conf_file, $matches)) {
1349
			continue;
1350
		}
1351
		$entry = array(
1352
		    'name' => ucfirst(strtolower($matches[1])),
1353
		    'path' => $conf_file,
1354
		    'descr' => $descr
1355
		);
1356
		if (file_exists($conf_file . ".default")) {
1357
			$entry['default'] = true;
1358
		}
1359
		$result[] = $entry;
1360
	}
1361

    
1362
	return $result;
1363
}
1364

    
1365
function pkg_get_default_repo() {
1366
	$repos = pkg_list_repos();
1367

    
1368
	foreach ($repos as $repo) {
1369
		if (isset($repo['default'])) {
1370
			return $repo;
1371
		}
1372
	}
1373

    
1374
	/* No default found, return the first one */
1375
	return ($repos[0]);
1376
}
1377

    
1378
/* List available repos on a format to be used by selectors */
1379
function pkg_build_repo_list() {
1380
	$repos = pkg_list_repos();
1381
	$list = array();
1382

    
1383
	foreach ($repos as $repo) {
1384
		$list[$repo['name']] = $repo['descr'];
1385
	}
1386

    
1387
	return($list);
1388
}
1389

    
1390
/* Find repo by path */
1391
function pkg_get_repo_name($path) {
1392
	$repos = pkg_list_repos();
1393

    
1394
	$default = $repos[0]['name'];
1395
	foreach ($repos as $repo) {
1396
		if ($repo['path'] == $path) {
1397
			return $repo['name'];
1398
		}
1399
		if (isset($repo['default'])) {
1400
			$default = $repo['name'];
1401
		}
1402
	}
1403

    
1404
	/* Default */
1405
	return $default;
1406
}
1407

    
1408
/* Setup pkg.conf according current repo */
1409
function pkg_conf_setup() {
1410
	global $g;
1411

    
1412
	$pkg_conf_path = "/usr/local/etc/pkg.conf";
1413
	$conf = "/usr/local/etc/pkg/repos/{$g['product_name']}.conf";
1414
	if (!file_exists($conf)) {
1415
		return;
1416
	}
1417

    
1418
	$real_conf = readlink($conf);
1419

    
1420
	if (!$real_conf) {
1421
		return;
1422
	}
1423

    
1424
	$abi_file = str_replace('.conf', '.abi', $real_conf);
1425
	$altabi_file = str_replace('.conf', '.altabi', $real_conf);
1426

    
1427
	$pkg_conf = array();
1428
	if (file_exists($abi_file) && file_exists($altabi_file)) {
1429
		$abi = file_get_contents($abi_file);
1430
		$altabi = file_get_contents($altabi_file);
1431

    
1432
		$pkg_conf = array(
1433
			"ABI={$abi}",
1434
			"ALTABI={$altabi}"
1435
		);
1436
	}
1437

    
1438
	file_put_contents($pkg_conf_path, $pkg_conf);
1439
}
1440

    
1441
/* Switch between stable and devel repos */
1442
function pkg_switch_repo($path) {
1443
	global $g;
1444

    
1445
	safe_mkdir("/usr/local/etc/pkg/repos");
1446
	@unlink("/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1447
	@symlink($path, "/usr/local/etc/pkg/repos/{$g['product_name']}.conf");
1448

    
1449
	pkg_conf_setup();
1450

    
1451
	/* Update pfSense_version cache */
1452
	mwexec_bg("/etc/rc.update_pkg_metadata now");
1453
	return;
1454
}
1455

    
1456
$FQDN = "https://ews.netgate.com/pfupdate";
1457
$refreshinterval = (24 * 3600);	// 24 hours
1458
$idfile = "/var/db/uniqueid";
1459
$repopath = "/usr/local/share/{$g['product_name']}/pkg/repos";
1460
$configflename = "{$repopath}/{$g['product_name']}-repo-custom.conf";
1461

    
1462
// Update the list of available repositories from the server. This will allow migration to another
1463
// update repository should the existing one becomes unavailable
1464
function update_repos() {
1465
	global $g, $config, $idfile, $FQDN, $repopath;
1466

    
1467
	if (file_exists($idfile)) {
1468
		// If the custom repository definition does not exist, or is more than 24 hours old
1469
		// Fetch a copy from the server
1470
		if ( (! file_exists($configflename)) || ( time()-filemtime($configflename) > $refreshinterval)) {
1471
			if (function_exists('curl_version')) {
1472
				// Gather some information about the system so the proper repo can be returned
1473
				$nid = file_get_contents($idfile);
1474
				$serial = system_get_serial();
1475
				$arch = php_uname('p');
1476

    
1477
				$base_pkg = get_base_pkg_name();
1478

    
1479
				$info = get_pkg_info($base_pkg, true, true);
1480

    
1481
				$pkg_info = array();
1482
					foreach ($info as $item) {
1483
						if ($item['name'] == $base_pkg) {
1484
							$pkg_info = $item;
1485
							break;
1486
						}
1487
					}
1488

    
1489
				$version = $pkg_info['installed_version'];
1490
				$locale = $config['system']['language'];
1491
				// Architecture comes from the last word in the uname output
1492
				$arch = array_pop(explode(' ', php_uname('p')));
1493

    
1494
				$post = ['uid' => $nid,
1495
						 'language' => $locale,
1496
						 'serial' => $serial,
1497
						 'version' => $version,
1498
						 'arch' => $arch
1499
						];
1500

    
1501
				$ch = curl_init();
1502
				curl_setopt($ch, CURLOPT_HEADER, 0);
1503
				curl_setopt($ch, CURLOPT_VERBOSE, 0);
1504
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1505
				curl_setopt($ch, CURLOPT_USERAGENT, $g['product_name'] . '/' . $g['product_version']);
1506
				curl_setopt($ch, CURLOPT_URL, $FQDN);
1507
				curl_setopt($ch, CURLOPT_POST, true);
1508
				curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
1509
				curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,4);
1510
				$response = curl_exec($ch);
1511
				$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1512
				curl_close($ch);
1513

    
1514
				if ($status == 200) {
1515
					save_repo($response);
1516
				}
1517
			}
1518
		}
1519
	}
1520
}
1521

    
1522
// Parse the received JSON data and save the custom repository information
1523
function save_repo($json) {
1524
	global $repopath, $g;
1525
	$repo = json_decode($json, true);
1526

    
1527
	if (($repo != NULL) && isset($repo['abi']) && isset($repo['altabi']) && isset($repo['conf']) &&
1528
	    isset($repo['descr']) && isset($repo['name']) && (strlen($repo['conf']) > 10)) {
1529
		$basename = "{$repopath}/{$g['product_name']}-repo-custom.";
1530

    
1531
		file_put_contents($basename . "conf", base64_decode($repo['conf']));
1532
		file_put_contents($basename . "descr", $repo['descr']);
1533
		file_put_contents($basename . "abi", $repo['abi']);
1534
		file_put_contents($basename . "altabi", $repo['altabi']);
1535
		file_put_contents($basename . "name", $repo['name']);
1536
		file_put_contents($basename . "help", $repo['help']);
1537
	} else {
1538
		// If there was anything wrong with the custom repository definition, remove the help text
1539
		// to avoid possible confusion
1540
		if (file_exists($basename . "help")) {
1541
			unlink($basename . "help");
1542
		}
1543
	}
1544
}
1545

    
1546
?>
(40-40/60)