#!/usr/bin/env php
<?php
//	License for all code of this FreePBX module can be found in the license file inside the module directory
//	Copyright 2013 Schmooze Com Inc.
//

//include bootstrapper
$bootstrap_settings['freepbx_auth'] = false;
include_once '/etc/freepbx.conf';
if (!$bootstrap_settings['astman_connected']) {
	throw new \Exception(sprintf(_("Unable to connect to Asterisk Manager from %s, aborting"),__FILE__));
}

$meminfo = getSystemMemInfo();
if(!empty($meminfo['MemTotal'])) {
	$memt = preg_replace("/\D/","",$meminfo['MemTotal']);
	ini_set('memory_limit',$memt.'K');
} else {
	$memt = 0;
}
$nt = notifications::create($db);
$mems = isset($meminfo['SwapTotal']) ? preg_replace("/\D/","",$meminfo['SwapTotal']) : '';
if(empty($mems)) {
	$nt->add_warning('core', 'SWAP', _("No Swap"), _("Your system has no swap space. This should be fixed as soon as possible. Once fixed issue a reload to remove this message"));
} else {
	if($mems < 200000) {
		$nt->add_warning('core', 'SWAP', _("No Swap"), sprintf(_("The swap space of your system is too low (%s KB). You should have at least %s KB of swap space. This should be fixed as soon as possible. Once fixed issue a reload to remove this message"),$mems,200000));
	} else {
		$nt->delete('core', 'SWAP');
	}
}

$freepbx_conf = \FreePBX::create()->Freepbx_conf;

$loc = fpbx_which("asterisk");
if(empty($loc)) {
	throw new \Exception(_("Unable to find the Asterisk binary"));
} else {
	exec($loc . " -rx 'core show version'",$out,$ret);
	if($ret != 0) {
		throw new \Exception(_("Unable to connect to Asterisk through the CLI"));
	}
}
// Load the BMO Object
$bmo = FreePBX::create();
//Load Asterisk Manager into Memory
$bmo->astman->useCaching = true;
$bmo->Performance->Start("load_astdb");
$bmo->astman->LoadAstDB();
$bmo->Performance->Stop("load_astdb");
// $bmo->Performance->On();
$bmo->Performance->Start("trust_gpg");
$bmo->GPG->trustFreePBX();
$bmo->Performance->Stop();

$bmo->Performance->Start("check_html5");
$bmo->Media->getSupportedHTML5Formats();
$bmo->Performance->Stop();

$bmo->Performance->Start("retrieve_conf");
$bmo->Hooks->updateBMOHooks();

// BMO TODO: Move this to be part of BMO.
class connectdirs {

	var $nt;
	var $symlink_dirs;
	var $cp_dirs;

	var $cp_errors = '';
	var $symlink_error_modules = '';
	var $symlink_notice_modules = '';
	var $symlink_notice_modules_bin = '';
	var $symlink_notice_modules_agi = '';

	static function &create() {
		static $obj;
		if (!isset($obj)) {
			$obj = new connectdirs();
		}
		return $obj;
	}

	function __construct() {
		global $amp_conf;
		global $db;
		$this->symlink_dirs['bin']	= $amp_conf['AMPBIN'];
		$this->symlink_dirs['etc'] 	= $amp_conf['ASTETCDIR'];
		$this->symlink_dirs['images'] 	=
			$amp_conf['AMPWEBROOT'] . "/admin/images";
		$this->symlink_dirs['ari/modules']	=
			$amp_conf['AMPWEBROOT'] . "/recordings/modules";
		$this->symlink_dirs['ari/theme'] 	=
			$amp_conf['AMPWEBROOT'] . "/recordings/theme";
		$this->symlink_dirs['ari/js']		=
			$amp_conf['AMPWEBROOT'] . "/recordings/theme/js";
		$this->symlink_dirs['ari/images']	=
			$amp_conf['AMPWEBROOT'] . "/recordings/theme/images";
		$this->symlink_sound_dirs['sounds'] 	=
			$amp_conf['ASTVARLIBDIR'] . '/sounds';
			//TODO agi-bin needs to be symlinked in the future
		$this->cp_dirs['agi-bin'] 	= $amp_conf['ASTAGIDIR'];
		/** BIN IS SYMLINKED **/
		//$this->cp_dirs['bin'] = $amp_conf['AMPBIN'];
		$this->nt = notifications::create($db);
	}

	function symlink_assets($module) {
		global $amp_conf;

		// e.g. /var/www/html/admin/modules/ringgroups/assets
		// e.g. /var/www/html/admin/assets/ringgroups
		//
		$srcdir = $amp_conf['AMPWEBROOT'] . '/admin/modules/' . $module . '/assets';
		$targetdir = $amp_conf['AMPWEBROOT'] . "/admin/assets/" . $module;

		// if assets does not exist in the module then there is
		// no need to have a link to it.
		if (!is_dir($srcdir)) {
			if (is_link($targetdir)) {
				$this->err_unlink($targetdir);
			}
			return;
		} else {
			// This module has assets that need to be linked.
			// Is the target already a directory?
			if (is_dir($targetdir) && !is_link($targetdir)) {
				// This shouldn't have happened. Rename it out of the way.
				rename($targetdir, "$targetdir.badasset");
			}
			// The assets dir exists in the module
			// If it's not a symlink, create it.
			if (!is_link($targetdir)) {
				if (!symlink($srcdir, $targetdir)) {
					freepbx_log(FPBX_LOG_ERROR, "Can not symlink $srcdir to $targetdir");
				} else {
					// All good, we created the correct link
					return true;
				}
			}

			// We're here because the link already exists. Make sure that the symlink is linking to the correct place.
			if (!is_link($targetdir)) {
				throw new \Exception(_("Symlink error - %s should be linked to %s, and it isn't a link"),$targetdir,$srcdir);
				exit(255);
			}

			$dest = readlink($targetdir);
			if ($dest !== $srcdir) {
				// Wow. How did that happen?
				unlink($targetdir);
				if (!symlink($srcdir, $targetdir)) {
					freepbx_log(FPBX_LOG_ERROR, "Error replacing symlink $srcdir to $targetdir");
				}
			}
		}
	}

	function generate_less($module) {
		// e.g. /var/www/html/admin/modules/ringgroups/assets
		// e.g. /var/www/html/admin/assets/ringgroups
		FreePBX::create()->Less->generateModuleStyles($module);
	}

	function symlink_sound_dirs($moduledir) {
		$language_dirs = array();
		foreach ($this->symlink_sound_dirs as $subdir => $targetdir) {
			$dir = addslash($moduledir).$subdir;
			if (!is_dir($dir)) {
				continue;
			}
			$d = opendir($dir);
			while ($file = readdir($d)) {
				if ($file[0] != '.') {
					// If this is a directory, then put
					// it on the list of language
					// directories to process,
					// otherwise symlink it
					if (is_dir(addslash($dir).$file)) {
						$language_dirs[] = $file;
					} else {
						$this->do_symlink(addslash($dir).$file, addslash($targetdir).$file, $subdir, $moduledir);
					}
				}
			}
			closedir($d);
		        // If we found any langauge directories, then
			// check if they are installed on the target and
			// if so symlink them over.
		        foreach ($language_dirs as $lang) {
				if (!is_dir(addslash($targetdir).$lang)) {
					// out(sprintf(_("found language dir %s for %s, not installed on system, skipping"),$lang,basename($moduledir)));
					continue;
				}
				$d = opendir(addslash($dir).$lang);
				while ($file = readdir($d)) {
					if ($file[0] != '.') {
						$this->do_symlink(addslash($dir).addslash($lang).$file, addslash($targetdir).addslash($lang).$file, $subdir, $moduledir);
					}
				}
				closedir($d);
			}
		}
	}

	function symlink_subdirs($moduledir) {
		foreach ($this->symlink_dirs as $subdir => $targetdir) {
			$dir = addslash($moduledir).$subdir;
			if (is_dir($dir)) {
				$d = opendir($dir);
					while ($file = readdir($d)) {
					if ($file[0] != '.') {
						$this->do_symlink(addslash($dir).$file, addslash($targetdir).$file, $subdir, $moduledir);
					}
				}
				closedir($d);
			}
		}
	}

	function do_symlink($src, $dest, $subdir, $moduledir) {
		global $amp_conf;
		$f = basename($dest);
		if(in_array($f,array('amportal','fwconsole','module_admin','retrieve_conf','freepbx_setting','freepbx_engine','freepbx-cron-scheduler.php','gen_amp_conf.php'))) {
			$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("Cannot replace reserved file %s from module %s (Not allowed)"),$f, basename($moduledir));
			return;
		}
		if (file_exists_wrapper($dest)) {
			if ((!is_link($dest) || readlink($dest) != $src) && file_exists($src) && file_exists($dest) && !is_dir($dest) && (md5_file($src) == md5_file($dest))) {
				if(!@unlink($dest)) {
					freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot remove conflicting file %s. Check Permissions?'),$dest));
					$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("Cannot remove conflicting file %s (Bad Permissions)"),$dest);
				} else {
					if (!symlink($src, $dest)) {
						freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot symlink %s to %s. Check Permissions?'),$src,$dest));
						$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Bad Permissions)"),$dest,basename($moduledir),$subdir);
					}
				}
			} else if (!is_link($dest)) {
				//If the symlink error is coming from the etc directory then we move those files to backup
				if(preg_match('/^'.str_replace("/","\/",$amp_conf['ASTETCDIR']).'/',$dest) && is_writable($dest)) {
					if(!file_exists($amp_conf['ASTETCDIR'].'/backup')) {
						mkdir($amp_conf['ASTETCDIR'].'/backup');
					}
					$f = $amp_conf['ASTETCDIR'].'/backup/'.basename($dest).".bk.".time();
					rename($dest,$f);
					if (!symlink($src, $dest)) {
						freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot symlink %s to %s. Check Permissions?'),$src,$dest));
						$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Bad Permissions)"),$dest,basename($moduledir),$subdir);
					} else {
						$this->symlink_notice_modules .= "<br />&nbsp;&nbsp;&nbsp;".$dest;
					}
				} else if(preg_match('/^'.str_replace("/","\/",$amp_conf['AMPBIN']).'/',$dest) && is_writable($dest)) {
					if(!file_exists($amp_conf['AMPBIN'].'/backup')) {
						mkdir($amp_conf['AMPBIN'].'/backup');
					}
					$f = $amp_conf['AMPBIN'].'/backup/'.basename($dest).".bk.".time();
					rename($dest,$f);
					if (!symlink($src, $dest)) {
						freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot symlink %s to %s. Check Permissions?'),$src,$dest));
						$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Bad Permissions)"),$dest,basename($moduledir),$subdir);
					} else {
						$this->symlink_notice_modules_bin .= "<br />&nbsp;&nbsp;&nbsp;".$dest;
					}
				} else if(preg_match('/^'.str_replace("/","\/",$amp_conf['ASTAGIDIR']).'/',$dest) && is_writable($dest)) {
					if(!file_exists($amp_conf['ASTAGIDIR'].'/backup')) {
						mkdir($amp_conf['ASTAGIDIR'].'/backup');
					}
					$f = $amp_conf['ASTAGIDIR'].'/backup/'.basename($dest).".bk.".time();
					rename($dest,$f);
					if (!symlink($src, $dest)) {
						freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot symlink %s to %s. Check Permissions?'),$src,$dest));
						$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Bad Permissions)"),$dest,basename($moduledir),$subdir);
					} else {
						$this->symlink_notice_modules_agi .= "<br />&nbsp;&nbsp;&nbsp;".$dest;
					}
				} else {
					freepbx_log(FPBX_LOG_ERROR, sprintf(_('%s already exists, and is not a symlink!'),$dest));
					$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Already exists, not a link)"),$dest,basename($moduledir),$subdir);
				}
			} else if (readlink($dest) != $src) {
				//users need to be aware of symlink conflicts. We should attempt to resolve them properly though. So lets do that.
				freepbx_log(FPBX_LOG_ERROR, $dest.' already exists, and is linked to something else!');
				$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Already exists, linked to something else)"),$dest,basename($moduledir),$subdir);
			}
		} else {
			$targetdir = dirname($dest);
			if (!is_dir($targetdir)) {
				//The directory does not exist which can/could be bad but isn't.
				//It's mainly fw_ari old cruft here
				freepbx_log(FPBX_LOG_ERROR, sprintf(_("Tried to link %s to %s, but %s doesn't exist"),$src,$dest,$targetdir));
			} else {
				if (!symlink($src, $dest)) {
					freepbx_log(FPBX_LOG_ERROR, sprintf(_('Cannot symlink %s to %s. Check Permissions?'),$src,$dest));
					$this->symlink_error_modules .= "<br />&nbsp;&nbsp;&nbsp;".sprintf(_("%s from %s/%s (Bad Permissions)"),$dest,basename($moduledir),$subdir);
				}
			}
		}
	}

	function symlink_check_errors() {
		global $amp_conf;
		if ($this->symlink_error_modules) {
			$this->nt->add_error('retrieve_conf', 'SYMLINK', _("Symlink from modules failed"), sprintf(_("retrieve_conf failed to sym link: %s<br \>This can result in FATAL failures to your PBX. If the target file exists and not identical, the symlink will not occur and you should rename the target file to allow the automatic sym link to occur and remove this error, unless this is an intentional customization."),$this->symlink_error_modules));
		} else {
			$this->nt->delete('retrieve_conf', 'SYMLINK');
		}
		if($this->symlink_notice_modules) {
			$this->nt->add_notice('retrieve_conf', 'SYMLINKNOTICE', _("Symlink Conflict Resolved"),sprintf(_("retrieve_conf resolved a symlink with %s<br \>This is a notice to let you know that the original file was moved to %s, there is nothing more you need to do"),$this->symlink_notice_modules,$amp_conf['ASTETCDIR'].'/backup'));
		}
		if($this->symlink_notice_modules_bin) {
			$this->nt->add_notice('retrieve_conf', 'SYMLINKNOTICEBIN', _("Symlink Conflict Resolved"),sprintf(_("retrieve_conf resolved a symlink with %s<br \>This is a notice to let you know that the original file was moved to %s, there is nothing more you need to do"),$this->symlink_notice_modules,$amp_conf['AMPBIN'].'/backup'));
		}
		if($this->symlink_notice_modules_agi) {
			$this->nt->add_notice('retrieve_conf', 'SYMLINKNOTICEAGI', _("Symlink Conflict Resolved"),sprintf(_("retrieve_conf resolved a symlink with %s<br \>This is a notice to let you know that the original file was moved to %s, there is nothing more you need to do"),$this->symlink_notice_modules,$amp_conf['ASTAGIDIR'].'/backup'));
		}
	}

	function cp_subdirs($moduledir) {
		global $amp_conf;
		foreach ($this->cp_dirs as $subdir => $targetdir) {
			$dir = addslash($moduledir).$subdir;
			if(is_dir($dir)){
				foreach(listdir($dir) as $idx => $file){
					$sourcefile = $file;
					$filesubdir=str_replace($dir.'/', '', $file);
					$targetfile = addslash($targetdir).$filesubdir;

					if (file_exists_wrapper($targetfile)) {
						if (is_link($targetfile)) {
							if (!$this->err_unlink($targetfile)) {
								freepbx_log(FPBX_LOG_ERROR, sprintf(_("%s is a symbolic link, failed to unlink!"),$targetfile));
								break;
							}
						}
					}
					// OK, now either the file is a regular file or
					// isn't there, so proceed
					if ($this->err_copy($sourcefile,$targetfile)) {
						// copy was successful, make sure it has execute permissions
						chmod($targetfile,0755);
						$ampowner = $amp_conf['AMPASTERISKWEBUSER'];
						/* Address concerns carried over from amportal
						 * in FREEPBX-8268. If the apache user is different
						 * than the Asterisk user we provide permissions
						 * that allow both.
						 */
						$ampgroup =  $amp_conf['AMPASTERISKWEBUSER'] != $amp_conf['AMPASTERISKUSER'] ? $amp_conf['AMPASTERISKGROUP'] : $amp_conf['AMPASTERISKWEBGROUP'];
						chown($targetfile, $ampowner);
						chgrp($targetfile, $ampgroup);
					} else {
						freepbx_log(FPBX_LOG_ERROR, sprintf(_("%s failed to copy from module directory"),$targetfile));
					}
				}
			}
		}
	}

	function cp_check_errors() {
		if ($this->cp_errors) {
			$this->nt->add_error('retrieve_conf', 'CPAGIBIN', _("Failed to copy from module agi-bin"), sprintf(_("Retrieve conf failed to copy file(s) from a module's agi-bin dir: %s"),$this->cp_errors));
		} else {
			$this->nt->delete('retrieve_conf', 'CPAGIBIN');
		}
	}

	function add_cp_error($string) {
		$this->cp_errors .= $string;
	}

	// wrap copy with error handler
	//
	function err_copy($source, $dest) {
		$ret = false;
		set_error_handler("report_errors");
		//if were copying a directory, just mkdir the directory
		if (!is_link($dest) && !is_dir($dest)) {
			if(is_dir($source)){
				$ret = mkdir($dest,0754);
			}elseif(copy($source, $dest)) {
				$ret = chmod($dest,0754);
			}
		}
		restore_error_handler();
		return $ret;
	  }

	// wrap unlink with error handler
	function err_unlink($dest) {
		set_error_handler("report_errors");
		$ret = unlink($dest);
		restore_error_handler();
		return $ret;
	}
}

// I don't think this can be part of the class since it is called by an
// error function as a callback (otherwise, can move it into above).
//
function report_errors($errno, $errstr, $errfile, $errline) {
	global $db;
	$escaped_string = $db->escapeSimple($errstr);
	freepbx_log(FPBX_LOG_ERROR, sprintf(_("php reported: '%s' after copy or unlink attempt!"),$escaped_string));
	$conn_dirs = connectdirs::create();
	$conn_dirs->add_cp_error($errstr."\n");
}

//define("ASTERISK_CONF", "/etc/asterisk/asterisk.conf");
// Warning Banner now part of BMO - See WriteConfig.class.php

function showHelp() {
	out(_(_("Optional parameters:")),false);
	out("  --help, -h, -?           "._("Show this help"),false);
	out("  --debug                  "._("Enable debug output"),false);
	out("  --dry-run                "._("Don't actually do anything"),false);
}

// Adds a trailing slash to a directory, if it doesn't already have one
function addslash($dir) {
	return (($dir[ strlen($dir)-1 ] == '/') ? $dir : $dir.'/');
}


/********************************************************************************************************************/

$dryrun = false;
$run_install = false;
$skip_registry_checks = false;

// **** Make sure we have PEAR's GetOpts.php, and include it if we need to parse
//
if ($argc > 1) {
  outn(_("Checking for PEAR Console::Getopt.."));
  if (! @ include("Console/Getopt.php")) {
	  out(_("FAILED"));
	  fatal(sprintf(_("PEAR must be installed (requires Console/Getopt.php). Include path: %s"), ini_get("include_path")),true);
  }
  out(_("OK"));

  // **** Parse out command-line options
  $shortopts = "h?u:p:";
  $longopts = array("help","debug","dry-run","run-install","amportalconf=","skip-registry-checks");

  $args = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), $shortopts, $longopts);
  if (is_object($args)) {
	  // assume it's PEAR_ERROR
	  out($args->message);
	  exit(255);
  }

  foreach ($args[0] as $arg) {
	  switch ($arg[0]) {
		  case "--help": case "h": case "?":
			  showHelp();
			  exit(10);
		  break;
		  case "--dry-run":
			  out(_("Dry-run only, no files will be written"),false);
			  $dryrun = true;
		  break;
		  case "--debug":
        // set to false temporarily, not with freepbx_conf as we don't want it subsequently committed
        //
        $amp_conf['FPBXDBUGDISABLE'] = false;
			  debug(_("Debug mode enabled"),false);
		  break;
		  case "--run-install":
			  $run_install = true;
			  out(_("Running module install.php and install.sql scripts"),true);
		  break;
		  case "--amportalconf":
			  $amportalconf = $arg[1];
			  out(sprintf(_("Using %s configuration file"), $amportalconf),false);
		  break;
		  case "--skip-registry-checks":
			  $skip_registry_checks = true;
			  out(_("Skipping extension and destination registry checks"),true);
		  break;
	  }
  }
}

// Define the notification class for logging to the dashboard
//
$nt = notifications::create($db);
$con_dirs = connectdirs::create();

/*
*/
// Check and increase php memory_limit if needed and if allowed on the system
// TODO: should all be in bootstrap
$current_memory_limit = rtrim(ini_get('memory_limit'),'M');
$proper_memory_limit = '100';
if ($current_memory_limit < $proper_memory_limit) {
	if (ini_set('memory_limit',$proper_memory_limit.'M') !== false) {
		$nt->add_notice('core', 'MEMLIMIT', _("Memory Limit Changed"), sprintf(_("Your memory_limit, %sM, is set too low and has been increased to %sM. You may want to change this in you php.ini config file"),$current_memory_limit,$proper_memory_limit));
	} else {
		$nt->add_warning('core', 'MEMERR', _("Low Memory Limit"), sprintf(_("Your memory_limit, %sM, is set too low and may cause problems. FreePBX is not able to change this on your system. You should increase this to %sM in you php.ini config file"),$current_memory_limit,$proper_memory_limit),'http://wiki.freepbx.org/x/lgK3AQ');
	}
} else {
	$nt->delete('core', 'MEMLIMIT');
}


//Putting the core module last, to move outbound-allroutes
// last in from-internals-additional
if (array_key_exists('core', $active_modules)) {
        $core_tmp = $active_modules['core'];
        unset($active_modules['core']);
        $active_modules['core'] = $core_tmp;
}

// include any module global functions
// $active_modules is provided by bootstrap now
//
$bmo->Performance->Start("Working with active modules");
if(is_array($active_modules)){
	foreach($active_modules as $key => $module) {
		$module_list[] = $key;

		if ($run_install) {
			echo sprintf(_("Installing %s..."),$key)."\n";
			module_install($key);
			echo sprintf(_("Done installing %s"),$key)."\n";
		}

		// create symlinks for files in appropriate sub directories
		// don't symlink framework files, it is a special case module
		// that happens to have some conflicting names
		//
		if (!isset($module['modtype']) || $module['modtype'] != 'framework') {
			// don't copy or symlink from framework type modules as they are not real modules
			try {
				$bmo->Performance->Stamp("Symlinking files for ".$key);
				$con_dirs->symlink_subdirs( $amp_conf['AMPWEBROOT'].'/admin/modules/'.$key );
				$con_dirs->symlink_sound_dirs( $amp_conf['AMPWEBROOT'].'/admin/modules/'.$key );
				$con_dirs->symlink_assets($key);
				$bmo->Performance->Stamp("Finished symlinking");
				$bmo->Performance->Stamp("Copying Files for ".$key);
				$con_dirs->cp_subdirs( $amp_conf['AMPWEBROOT'].'/admin/modules/'.$key );
				$bmo->Performance->Stamp("Finished Copying Files");
				$bmo->Performance->Stamp("Generating CSS from LESS for ".$key);
				$con_dirs->generate_less($key); //generate less for said module if needed
				$bmo->Performance->Stamp("Finished Generating Less");
			} catch(\Exception $e) {
				echo sprintf(_("Unable to continue. %s in %s on line %s"),$e->getMessage(),$e->getFile(),$e->getLine());
				echo $e->getTraceAsString();
				exit(255);
			}
		}

	}
}
$bmo->Performance->Stop();

$bmo->Performance->Start("Generating all compiled CSS files from less");
$bmo->Less->generateMainStyles();
$bmo->Performance->Stop();

// Now that we have done all the symlinks and copies, we check and report if there were any errors
//
$con_dirs->symlink_check_errors();
$con_dirs->cp_check_errors();

//once we have all the connected files in place, lets compress the css
if ($amp_conf['DISABLE_CSS_AUTOGEN'] != true) {
	compress_framework_css();
}

// create an object of the extensions class
require_once($amp_conf['AMPWEBROOT']."/admin/libraries/extensions.class.php");
$ext = new extensions;

if ($amp_conf['DISABLECUSTOMCONTEXTS']) {
  $ext->disableCustomContexts(true);
}

// create objects for any module classes
// currently only 1 class can be declared per module, not sure if that will be an issue
if(isset($module_list) && is_array($module_list)){
	foreach($module_list as $active_module) {
		$classname = $active_module."_conf";
		if(class_exists($classname)) {
			${$classname} = new $classname;
		}
	}
}

$engineinfo = engine_getinfo();
if($engineinfo['version'] == 0){
	fatal(sprintf(_("retreive_conf failed to get engine information and cannot configure up a softwitch with out it. Error: %s"),$engineinfo['engine']),true);
}
// was setting these variables before, assume we still need them
$engine = $engineinfo['engine'];
$version = $engineinfo['version'];
if (version_compare($version, "11", "lt") || version_compare($version, "17", "ge")) {
	fatal(_("Running an unsupported version of Asterisk. Supported Asterisk versions: 11, 13, 14, 15, 16. Detected Asterisk version: ".$version));
}
$chan_dahdi = ast_with_dahdi();


// If BROWSER_STATS is set to true (default) and we have never provided a notice (NOTICE_BROWSER_STATS false) then do so one time only so
// they are aware and can choose to opt out.
if (!$amp_conf['NOTICE_BROWSER_STATS'] && $amp_conf['BROWSER_STATS']) {
  $nt->add_notice('framework', 'BROWSER_STATS', _("Collecting Anonymous Browser Stats"), _("The FreePBX project is collecting anonymous browser statistics using google analytics. These are used to focus development efforts based on real user input. All information is anonymous. You can disable this in Advanced Settings with the Browser Stats setting."));
  $freepbx_conf->set_conf_values(array('NOTICE_BROWSER_STATS' => true), true, true);
}


if (!$freepbx_conf->conf_setting_exists('AST_FUNC_DEVICE_STATE')) {
  // AST_FUNC_DEVICE_STATE
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function DEVICE_STATE';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_DEVICE_STATE',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_FUNC_EXTENSION_STATE')) {
  // AST_FUNC_EXTENSION_STATE
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function EXTENSION_STATE';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_EXTENSION_STATE',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_FUNC_PRESENCE_STATE')) {
  // AST_FUNC_PRESENCE_STATE
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function PRESENCE_STATE';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_PRESENCE_STATE',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_FUNC_SHARED')) {
  // AST_FUNC_SHARED
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function SHARED';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_SHARED',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_FUNC_CONNECTEDLINE')) {
  // AST_FUNC_CONNECTEDLINE
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function CONNECTEDLINE';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_CONNECTEDLINE',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_FUNC_MASTER_CHANNEL')) {
  // AST_FUNC_MASTER_CHANNEL
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Function MASTER_CHANNEL';
  $set['description'] = "Set to the function name if the function is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_FUNC_MASTER_CHANNEL',$set);
}
if (!$freepbx_conf->conf_setting_exists('AST_APP_VQA')) {
  // AST_APP_VQA
  //
  $set['value'] = '';
  $set['defaultval'] =& $set['value'];
  $set['options'] = '';
  $set['readonly'] = 1;
  $set['hidden'] = 1;
  $set['level'] = 10;
  $set['module'] = '';
  $set['category'] = 'Internal Use';
  $set['emptyok'] = 1;
  $set['name'] = 'Asterisk Application VQA';
  $set['description'] = "Set to the application name if the application is present in this Asterisk install";
  $set['type'] = CONF_TYPE_TEXT;
  $freepbx_conf->define_conf_setting('AST_APP_VQA',$set);
}
// Since modules can be loaded in a current version, we check these each time
// DEVICE_STATE exists in two flavors, DEVSTATE and DEVICE_STATE, the latter is the official one
// but back ports exists with the former with both.
//
if ($astman->func_exists('DEVICE_STATE')) {
  $update_arr['AST_FUNC_DEVICE_STATE'] = 'DEVICE_STATE';
} else {
  $update_arr['AST_FUNC_DEVICE_STATE'] = $astman->func_exists('DEVSTATE') ? 'DEVSTATE' : '';
  // If they don't have DEVICE_STATE loaded in some form, force USEDEVSTATE off
  if ($amp_conf['USEDEVSTATE'] && !$update_arr['AST_FUNC_DEVICE_STATE']) {
    $update_arr['USEDEVSTATE'] = false;
  }
}
$func_arr = array('EXTENSION_STATE', 'PRESENCE_STATE', 'SHARED', 'CONNECTEDLINE', 'MASTER_CHANNEL');
foreach ($func_arr as $func) {
  $update_arr['AST_FUNC_'.$func] = $astman->func_exists($func) ? $func : '';
}

$app_arr = array('VQA');
foreach ($app_arr as $app) {
  $update_arr['AST_APP_'.$app] = $astman->app_exists($app) ? $app : '';
}
$freepbx_conf->set_conf_values($update_arr, true, true);

$nt = notifications::create($db);

$conf_change = _("Conference Room App Changed");
$conf_change_desc = _("Your Conference Room App (ASTCONFAPP) was automatically changed from %s to %s because %s is not installed on your Asterisk installation");

$conf_missing = _("No Conference Room App");
$conf_missing_desc = _("Neither app_meetme nor app_confbridge is configured in Asterisk, conferencing, paging and other functionality will not work properly");

if ($amp_conf['ASTCONFAPP'] == 'app_meetme' && !$astman->app_exists('meetme')) {
	if ($astman->app_exists('confbridge')) {
		$freepbx_conf->set_conf_values(array('ASTCONFAPP' => 'app_confbridge'), true, true);
  	$nt->add_notice('framework', 'ASTCONFAPPCHG', $conf_change, sprintf($conf_change_desc,'app_meetme','app_confbridge','app_meetme'));
		$nt->delete('framework', 'ASTCONFAPPMISSING');
	} else {
		$nt->add_error('framework', 'ASTCONFAPPMISSING', $conf_missing, $conf_missing_desc);
	}
} elseif ($amp_conf['ASTCONFAPP'] == 'app_confbridge' && !$astman->app_exists('confbridge')) {
	if ($astman->app_exists('meetme')) {
		$freepbx_conf->set_conf_values(array('ASTCONFAPP' => 'app_meetme'), true, true);
  	$nt->add_notice('framework', 'ASTCONFAPPCHG', $conf_change, sprintf($conf_change_desc,'app_confbridge','app_meetme','app_confbridge'));
		$nt->delete('framework', 'ASTCONFAPPMISSING');
	} else {
		$nt->add_error('framework', 'ASTCONFAPPMISSING', $conf_missing, $conf_missing_desc);
	}
} else {
	$nt->delete('framework', 'ASTCONFAPPMISSING');
}

// Check for and report any extension conflicts
//

$extens_ok = true;
$dests_ok = true;

$my_hash = array_flip($module_list);
$bmo->Performance->Start("extenconflicts");
$my_prob_extens = $skip_registry_checks ? false : framework_list_extension_conflicts($my_hash);
$bmo->Performance->Stop();

if (empty($my_prob_extens)) {
	$nt->delete('retrieve_conf', 'XTNCONFLICT');
} else {
	$previous = null;
	$str = null;
	$count = 0;
	foreach ($my_prob_extens as $extens) {
		foreach ($extens as $exten => $details) {
			if ($exten != $previous) {
				$str .=  _("Extension").": $exten:<br />";
				$count++;
			}
			$str .= sprintf("%8s: %s<br />",$details['status'], $details['description']);
			$previous = $exten;
		}
	}
	$nt->add_error('retrieve_conf', 'XTNCONFLICT', sprintf(_("There are %s conflicting extensions"),$count), $str);
	$extens_ok = false;
}

// Check for and report any bogus destinations
//
$bmo->Performance->Start("listproblems");
$my_probs = $skip_registry_checks ? false : framework_list_problem_destinations($my_hash, !$amp_conf['CUSTOMASERROR']);
$bmo->Performance->Stop();

if (empty($my_probs)) {
	$nt->delete('retrieve_conf', 'BADDEST');
} else {
	$results = array();
	$count = 0;
	$str = null;
	foreach ($my_probs as $problem) {
		//print_r($problem);
		$results[$problem['status']][] = $problem['description'];
		$count++;
	}
	foreach ($results as $status => $subjects) {
		$str .= sprintf(_("DEST STATUS: %s%s"),$status,"\n");
		foreach ($subjects as $subject) {
			//$str .= $subject."<br />";
			$str .= "   ".$subject."\n";
		}
	}
	$nt->add_error('retrieve_conf', 'BADDEST', sprintf(_("There are %s bad destinations"),$count), $str);
	$dests_ok = false;
}

if ((!$extens_ok && $amp_conf['XTNCONFLICTABORT']) || (!$dests_ok && $amp_conf['BADDESTABORT'])) {
	out(_("Aborting reload because extension conflicts or bad destinations"));
	exit(20);
}

// Generate an extension map of all extensions on the system
$bmo->Performance->Start("extmap");
framework_set_extmap();
$bmo->Performance->Stop();

// Dialplan Hook processing moved to BMO.
$bmo->Performance->Start("getAllDialplanHooks");
try {
	$hooks = $bmo->DialplanHooks->getAllHooks($active_modules);
} catch(\Exception $e) {
	echo sprintf(_("Unable to continue. %s in %s on line %s"),$e->getMessage(),$e->getFile(),$e->getLine())."\n";
	echo $e->getTraceAsString();
	exit(255);
}
$bmo->Performance->Stop();

$bmo->Performance->Start("processDialplanHooks");
if (is_array($hooks)) {
	try {
		$bmo->DialplanHooks->processHooks($engine, $hooks);
	} catch(\Exception $e) {
		echo sprintf(_("Unable to continue. %s in %s on line %s"),$e->getMessage(),$e->getFile(),$e->getLine())."\n";
		echo $e->getTraceAsString();
		exit(255);
	}
}
$bmo->Performance->Stop();

// extensions_additional.conf
// create the from-internal-additional contexts so other can add to it
$ext->add('from-internal-additional', 'h', '', new ext_hangup(''));
$ext->add('from-internal-noxfer-additional', 'h', '', new ext_hangup(''));

// Write extensions_additional.conf!
$bmo->Performance->Start("extensions_additional");
try {
	$bmo->WriteConfig->writeConfig($ext->get_filename(), $ext->generateConf());
} catch(\Exception $e) {
	echo sprintf(_("Unable to continue. %s in %s on line %s"),$e->getMessage(),$e->getFile(),$e->getLine());
	echo $e->getTraceAsString();
	exit(255);
}
$bmo->Performance->Stop();


// Output any other configuration files from other modules.
$bmo->Performance->Start("processFileHooks");
$bmo->FileHooks->processFileHooks($module_list);
$bmo->Performance->Stop();

// Now we write on amportal.conf if it is writable, which allows legacy applications in the
// eco-system to take advantage of the settings.
// we write out the error message here instead of in freepbx_settings so that we don't hit the db every single page load
//
if ($freepbx_conf->amportal_canwrite()) {
  file_put_contents('/etc/amportal.conf',$freepbx_conf->amportal_generate(true));
  $nt->delete('framework', 'AMPORTAL_NO_WRITE');
} elseif (!$nt->exists('framework', 'AMPORTAL_NO_WRITE')) {
  $nt->add_error('framework', 'AMPORTAL_NO_WRITE', _("amportal.conf not writeable"), _("Your amportal.conf file is not writeable. FreePBX is running in a crippled mode until changed. You can run 'amportal chown' from the Linux command line to rectify this."),true);
}

// Let's move some more of our checks to retrieve_conf so that we are not constantly checking these on page loads
//

// Warn about default Manager Interface Password
//
if ($amp_conf['AMPMGRPASS'] == $freepbx_conf->get_conf_default_setting('AMPMGRPASS')) {
  if (!$nt->exists('core', 'AMPMGRPASS')) {
	  $nt->add_warning('core', 'AMPMGRPASS', _("Default Asterisk Manager Password Used"), _("You are using the default Asterisk Manager password that is widely known, you should set a secure password"),'config.php?display=advancedsettings#ASTMANAGERHOST');
  }
} else {
	$nt->delete('core', 'AMPMGRPASS');
}

// Warn about default ARI Admin Password
//
if ($amp_conf['ARI_ADMIN_PASSWORD'] == $freepbx_conf->get_conf_default_setting('ARI_ADMIN_PASSWORD')) {
  if (!$nt->exists('ari', 'ARI_ADMIN_PASSWORD')) {
	  $nt->add_warning('ari', 'ARI_ADMIN_PASSWORD', _("Default ARI Admin password Used"), _("You are using the default ARI Admin password that is widely known, you should change to a new password. Do this in Advanced Settings"));
  }
} else {
	$nt->delete('ari', 'ARI_ADMIN_PASSWORD');
}

// Warn about default Database Password
//
if ($amp_conf['AMPDBPASS'] == $freepbx_conf->get_conf_default_setting('AMPDBPASS')) {
  if (!$nt->exists('core', 'AMPDBPASS')) {
	  $nt->add_warning('core', 'AMPDBPASS', _("Default SQL Password Used"), _("You are using the default SQL password that is widely known, you should set a secure password"));
  }
} else {
	$nt->delete('core', 'AMPDBPASS');
}

// Warn if in deviceanduser mode and not using DYNAMICHINTS
//
if ($amp_conf['AMPEXTENSIONS'] == 'deviceanduser' && !$amp_conf['DYNAMICHINTS']) {
  if (!$nt->exists('framework', 'NO_DYNAMICHINTS')) {
	  $nt->add_warning('framework', 'NO_DYNAMICHINTS', _("Device & User Hints Issue"), _("You are set to Device and User mode but are not set to 'Dynamically Generate Hints' which can result in improper phone state behavior. This can be changed on the Advanced Settings page, check the tooltip for specific configuration details."));
  }
} else {
	$nt->delete('framework', 'NO_DYNAMICHINTS');
}

/* file_exists_wrapper()
 * wrapper for file_exists() with the following additonal functionality.
 * if the file is a symlink, it will check if the link exists and if not
 * it will try to remove this file. It returns a false (file does not exists)
 * if the file is successfully removed, true if not. If not a symlink, just
 * returns file_exists()
 */
function file_exists_wrapper($string) {
	if (is_link($string)) {
		$linkinfo = readlink($string);
		if ($linkinfo === false) {
			//TODO: throw error?
			return !unlink($string);
		} else {
			if (file_exists($linkinfo)) {
				return true;
			} else {
				return !unlink($string);
			}
		}
	} else {
		return file_exists($string);
	}
}

//based on: http://snippets.dzone.com/posts/show/155
function listdir($directory, $recursive=true) {
	$array_items = array();
		if ($handle = opendir($directory)) {
			while (false !== ($file = readdir($handle))) {
				if ($file != "." && $file != "..") {
					if (is_dir($directory. "/" . $file)) {
						if($recursive) {
							$array_items = array_merge($array_items, listdir($directory. "/" . $file, $recursive));
						}
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				}else{
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				}
			}
		}
		closedir($handle);
	}
	return array_reverse($array_items);//reverse so that we get directories BEFORE the files that are in them
}

/** Check if there is a cron job scheduled to run.
 *  If the scheduling job is found then all is good.
 *  If the scheduling job is not found, it will be added
 *  and a notification will be sent.
 */
function install_cron_scheduler() {
	global $amp_conf;
	global $nt;

	try {
		$nt->delete('retrieve_conf', 'CRONMGR');
		//force autoloader to load
		$freepbxCron = \FreePBX::create()->Cron($amp_conf['AMPASTERISKWEBUSER']);
		if (posix_geteuid() == 0) {
			$rootCron = new \FreePBX\Cron('root');
			foreach($rootCron->getAll() as $cron) {
				$str = str_replace("/", "\/", $amp_conf['AMPBIN']."/freepbx-cron-scheduler.php");
				if(preg_match("/".$str."/i",$cron,$matches)) {
					$rootCron->remove($cron);
				}
			}
		} else {
			$userArray = posix_getpwuid(posix_geteuid());
			if ($userArray['name'] != $amp_conf['AMPASTERISKWEBUSER']) {
				// we are not the user we need to be,
				// and we aren't root, so there is nothing
				// we can do but report the error condition
				$nt->add_critical("FRAMEWORK", "CRON_UPDATE", _("Unable to create or update cron jobs"), sprintf(_("The PBX is running as %s but according to Advanced Settings it should be running as %. Therefore no cron jobs have been created. This can cause serious problems with your PBX in the future if it is not resolved"),$userArray['name'],$amp_conf['AMPASTERISKWEBUSER']));
				return true;
			} else {
				$nt->delete("FRAMEWORK", "CRON_UPDATE");
			}
		}
		$exists = false;
		foreach($freepbxCron->getAll() as $cron) {
			$str = str_replace("/", "\/", $amp_conf['AMPBIN']."/freepbx-cron-scheduler.php");
			if(preg_match("/freepbx-cron-scheduler.php$/",$cron)) {
				if(!preg_match("/".$str."$/i",$cron)) {
					$freepbxCron->remove($cron);
				}
			}

			if(preg_match("/".$str."/i",$cron,$matches)) {
				if($exists) {
					//remove multiple entries (if any)
					$freepbxCron->remove($cron);
				}
				$exists = true;
			}
		}
		if(!$exists) {
			$freepbxCron->add(array(
				"command" => $amp_conf['AMPBIN']."/freepbx-cron-scheduler.php",
				"minute" => rand(0,59)
			));
		}

		$exists = false;
		foreach($freepbxCron->getAll() as $cron) {
			$str = str_replace("/", "\/", $amp_conf['AMPSBIN']."/fwconsole util cleanplaybackcache -q");
			if(preg_match("/fwconsole util cleanplaybackcache -q$/",$cron)) {
				if(!preg_match("/".$str."$/i",$cron)) {
					$freepbxCron->remove($cron);
				}
			}

			if(preg_match("/".$str."/i",$cron,$matches)) {
				if($exists) {
					//remove multiple entries (if any)
					$freepbxCron->remove($cron);
				}
				$exists = true;
			}
		}
		if(!$exists) {
			$freepbxCron->add(array(
				"command" => $amp_conf['AMPSBIN']."/fwconsole util cleanplaybackcache -q",
				"minute" => rand(0,59)
			));
		}
	} catch(\Exception $e) {
		$nt->add_error('retrieve_conf', 'CRONMGR', _("Failed to check crontab for cron manager"), sprintf(_("crontab returned %s error code when checking for crontab entries to start freepbx-cron-scheduler.php crontab manager"),$e->getMessage()));
	}
}

// Check and install the freepbx-cron-scheduler.php manager
//
install_cron_scheduler();


// run retrieve_conf_post_custom
// If the following file exists, it will be run. This allows customization to be run automatically after the normal
// processing. Caution should be taken using this as it is only deisgned for expert usage. Errors in the code will
// have bad consequences and can cripple the system.
//
if ($amp_conf['AMPLOCALBIN']) {
$post_custom = $amp_conf['AMPLOCALBIN'].'/retrieve_conf_post_custom';
	if (file_exists($post_custom)) {
		outn(sprintf(_("Found script %s, executing.."), $post_custom));
		include($post_custom);
		out(_("OK"));
	}
}

/* As of Asterisk 1.4.16 or there abouts, a missing #include file will make the reload fail. So
   we need to make sure that we have such for everything that is in our configs. We will simply
	 look for the #include statements and touch the files vs. trying to inventory everything we may
	 need and then forgetting something.
*/

$output = array();
exec("grep '#include' ".$amp_conf['ASTETCDIR']."/*.conf | sed 's/;.*//; s/#include//'",$output,$retcode);
if ($retcode != 0) {
	error("Error code $retcode: trying to search for missing #include files");
}

foreach($output as $file) {
	if (trim($file) == '') {
		continue;
	}
	$parse1 = explode(':',$file);
	$parse2 = explode(';',$parse1[1]);
	$rawfile = trim($parse2[0]);
	if ($rawfile == '') {
		continue;
	}

	$target = ($rawfile[0] == '/') ? $rawfile : $amp_conf['ASTETCDIR']."/$rawfile";

	if (!file_exists($target)) {
		$output = array();
		exec("touch $target", $output, $retcode);
		if ($retcode != 0) {
			error("Error code $retcode: trying to create empty file $target");
		}
	}
}

// Some later versions of Aserisk require the existence of a cdr.conf file or no CDR
// incuding MySQL logging will work (see #3940)
//
$target = $amp_conf['ASTETCDIR']."/cdr.conf";
if (!file_exists($target)) {
  $output = array();
  exec("touch $target", $output, $retcode);
  if ($retcode != 0) {
	  error("Error code $retcode: trying to create empty file $target");
  }
}

// **** Set reload flag for AMP admin
needreload();
if (isset($amp_conf["AMPWEBADDRESS"]) && $amp_conf["AMPWEBADDRESS"])
{
	out(sprintf(_("Please update your modules and reload Asterisk by visiting %s"), "http://".$amp_conf["AMPWEBADDRESS"]."/admin"),false);
}
else
{
	out(_("Please update your modules and reload Asterisk by browsing to your server."),false);
}

if(!FreePBX::Config()->get('SIGNATURECHECK')) {
	$nt->add_notice('freepbx', 'SIGNATURE_CHECK', _('Signature checking is disabled'), _('FreePBX Module Signature checking has been disabled. Your system could be exposed to security vulnerabilities from compromised or tampered code'));
} else {
	$nt->delete('freepbx', 'SIGNATURE_CHECK');
	$bmo->Performance->Start("Signature Checks");
	$external = true;
	if(file_exists($amp_conf['AMPBIN']."/fwconsole")) {
		if(!is_executable($amp_conf['AMPBIN']."/fwconsole")) {
			if(!@chmod($amp_conf['AMPBIN']."/fwconsole", 0755)) {
				$external = false;
			}
		}
	} else {
		$external = false;
	}
	if($external) {
		exec($amp_conf['AMPBIN']."/fwconsole util signaturecheck > /dev/null 2>&1 &");
	} else {
		module_functions::create()->getAllSignatures(false);
	}
	$bmo->Performance->Stop();
}

$nt->delete('retrieve_conf', 'FATAL');

$bmo->Performance->Stop("retrieve_conf");
