root/freepbx/branches/2.9/install_amp

Revision 11828, 39.8 kB (checked in by p_lindheimer, 2 years ago)

fixes #4940 process install-fop as intended

  • Property svn:mime-type set to text/plain
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/php -q
2 <?php
3 //This file is part of FreePBX.
4 //
5 //    FreePBX is free software: you can redistribute it and/or modify
6 //    it under the terms of the GNU General Public License as published by
7 //    the Free Software Foundation, either version 2 of the License, or
8 //    (at your option) any later version.
9 //
10 //    FreePBX is distributed in the hope that it will be useful,
11 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //    GNU General Public License for more details.
14 //
15 //    You should have received a copy of the GNU General Public License
16 //    along with FreePBX.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 //    Copyright 2006, qldrob
19 //    Copyright 2009, Bandwith.com
20 //
21 require_once ("libfreepbx.install.php");
22 require_once(dirname(__FILE__) . '/amp_conf/htdocs/admin/libraries/php-upgrade.functions.php');
23 require_once(dirname(__FILE__) . '/amp_conf/htdocs/admin/libraries/sql.functions.php');
24 require_once(dirname(__FILE__) . '/amp_conf/htdocs/admin/libraries/utility.functions.php');
25
26 # constants
27 define("AMP_CONF", "/etc/amportal.conf");
28 define("ASTERISK_CONF", "/etc/asterisk/asterisk.conf");
29 define("UPGRADE_DIR", dirname(__FILE__)."/upgrades");
30 define("MODULE_DIR", dirname(__FILE__)."/amp_conf/htdocs/admin/modules/");
31
32 # semi constants
33 $webroot  = "/var/www/html";
34 $fopwebroot = "";       // if blank, will use $webroot/panel
35 $ampsbin_dir = "/usr/local/sbin"; // default if not set
36 $ampbin_dir = "/var/lib/asterisk/bin";
37 $asterisk_user  = "asteriskuser";
38 $asterisk_pass  = "amp109";
39
40 /********************************************************************************************************************/
41
42 function showHelp() {
43   out("Optional parameters:",false);
44   out("  --help, -h, -?           Show this help",false);
45   out("  --dbhost <ip address>    Use a remote database server",false);
46   out("  --dbname databasename    Use database name specified, instead of 'asterisk'",false);
47   out("  --username <user>        Use <user> to connect to db and write config",false);
48   out("  --password <pass>        Use <pass> to connect to db and write config",false);
49   out("  --fopwebroot <path>      Web path where fop will be installed",false);
50   out("  --webroot <path>         Web root where FreePBX will be installed",false);
51   out("  --cgibin <path>          Path where cgi-bin's lives",false);
52   out("  --bin <path>             Path of asterisk binaries",false);
53   out("  --sbin <path>            Path of system admin binaries",false);
54   out("  --asteriskuser <user>    Asterisk Manager username",false);
55   out("  --asteriskpass <pass>    Asterisk Manager password",false);
56   out("  --systemconfig <path>    System config files",false);
57   out("  --debug                  Enable debug output",false);
58   out("  --dry-run                Don't actually do anything",false);
59   out("  --force-version <ver>    Force upgrade from version <ver>",false);
60   out("  --skip-module-install    Don't run install scripts for packaged modules, the files are still loaded.",false);
61   out("                           In a development environment you may not want the install scripts run.",false);
62   out("  --no-files               Just run updates without installing files",false);
63   out("  --force-overwrite        Don't ask if it is ok to overwrite modified files, assume yes for all.",false);
64   out("  --install-moh            Install default music-on-hold files (normally doesn't, unless ",false);
65   out("                           it's a new installation)",false);
66   out("  --install-fop false      Don't install FOP and don't display it anywhere on the interface",false);
67   out("  --dev-links              Make links to files in the source directory instead of copying",false);
68   out("                           (intended for developers only)",false);
69   out("  --my-svn-is-correct      Ignore Asterisk version, assume it is correct",false);
70   out("  --engine <name>          Use the specified PBX Engine ('asterisk')",false);
71 }
72
73 function install_parse_amportal_conf($filename) {
74   $file = file($filename);
75   foreach ($file as $line) {
76     if (preg_match("/^\s*([a-zA-Z0-9]+)\s*=\s*(.*)\s*([;#].*)?/",$line,$matches)) {
77       $conf[ $matches[1] ] = $matches[2];
78     }
79   }
80
81   // use same defaults as function.inc.php
82   if ( !isset($conf["AMPDBENGINE"]) || ($conf["AMPDBENGINE"] == "")) {
83     $conf["AMPDBENGINE"] = "mysql";
84   }
85  
86   if ( !isset($conf["AMPDBNAME"]) || ($conf["AMPDBNAME"] == "")) {
87     $conf["AMPDBNAME"] = "asterisk";
88   }
89  
90   if ( !isset($conf["AMPENGINE"]) || ($conf["AMPENGINE"] == "")) {
91     $conf["AMPENGINE"] = "asterisk";
92   }
93
94   return $conf;
95 }
96
97 function install_parse_asterisk_conf($filename) {
98   $file = file($filename);
99   foreach ($file as $line) {
100     if (preg_match("/^\s*([a-zA-Z0-9]+)\s* => \s*(.*)\s*([;#].*)?/",$line,$matches)) {
101       $conf[ $matches[1] ] = $matches[2];
102     }
103   }
104   // Now set defaults if not set (although asterisk should fail if not set but at least
105   // it will get the setup somewhat right
106   //
107   if (!isset($asterisk_conf['astetcdir']))    { $asterisk_conf['astetcdir']    = "/etc/asterisk"; }
108   if (!isset($asterisk_conf['astmoddir']))    { $asterisk_conf['astmoddir']    = "/usr/lib/asterisk/modules"; }
109   if (!isset($asterisk_conf['astvarlibdir'])) { $asterisk_conf['astvarlibdir'] = "/var/lib/asterisk"; }
110   if (!isset($asterisk_conf['astagidir']))    { $asterisk_conf['astagidir']    = "/var/lib/asterisk/agi-bin"; }
111   if (!isset($asterisk_conf['astspooldir']))  { $asterisk_conf['astspooldir']  = "/var/spool/asterisk"; }
112   if (!isset($asterisk_conf['astrundir']))    { $asterisk_conf['astrundir']    = "/var/run/asterisk"; }
113   if (!isset($asterisk_conf['astlogdir']))    { $asterisk_conf['astlogdir']    = "/var/log/asterisk"; }
114
115   return $conf;
116 }
117
118 function write_amportal_conf($filename, $conf) {
119   $file = file($filename);
120   // parse through the file
121   foreach (array_keys($file) as $key) {
122     if (preg_match("/^\s*([a-zA-Z0-9]+)\s*=\s*(.*)\s*([;#].*)?/",$file[$key],$matches)) {
123       // this is an option=value line
124       if (isset($conf[ $matches[1] ])) {
125         // rewrite the line, if we have this in $conf
126         $file[$key] = $matches[1]."=".$conf[ $matches[1] ]."\n";
127         // unset it so we know what's new
128         unset($conf[ $matches[1] ]);
129       }
130     }
131   }
132  
133   // add new entries
134   foreach ($conf as $key=>$val) {
135     $file[] = $key."=".$val."\n";
136   }
137  
138   // write the file
139   if (!$fd = fopen($filename, "w")) {
140     fatal("Could not open ".$filename." for writing");
141   }
142   fwrite($fd, implode("",$file));
143   fclose($fd);
144 }
145
146 function ask_overwrite($file1, $file2) {
147   global $check_md5s;
148   do {
149     out($file2." has been changed from the original version.");
150     outn("Overwrite (y=yes/a=all/n=no/d=diff/s=shell/x=exit)? ");
151     $key = fgets(STDIN,1024);
152     switch (strtolower($key[0])) {
153       case "y": return true;
154       case "a": $check_md5s=false; return true;
155       case "n": return false;
156       case "d":
157         out("");
158         // w = ignore whitespace, u = unified
159         passthru("diff -wu ".escapeshellarg($file2)." ".escapeshellarg($file1));
160       break;
161       case "s":
162         if (function_exists("pcntl_fork")) {
163           out("");
164           $shell = (isset($_ENV["SHELL"]) ? $_ENV["SHELL"] : "/bin/bash");
165           out("Dropping to shell. Type 'exit' to return");
166           out("-> Original file:  ".$file2);
167           out("-> New file:       ".$file1);
168          
169           $pid = pcntl_fork();
170           if ($pid == -1) {
171             out("[ERROR] cannot fork");
172           } else if ($pid) {
173             // parent
174             pcntl_waitpid($pid, $status);
175             // we wait till the child exits/dies/whatever
176           } else {
177             pcntl_exec($shell, array(), $_ENV);
178           }
179          
180           out("Returned from shell");
181         } else {
182           out("[ERROR] PHP not built with process control (--enable-pcntl) support: cannot spawn shell");
183         }
184        
185       break;
186       case "x":
187         out("-> Original file:  ".$file2);
188         out("-> New file:       ".$file1);
189         out("Exiting install program.");
190         exit(1);
191       break;
192     }
193     out("");
194   } while(1);
195 }
196
197 /** Write AMP-generated configuration files
198  */
199 function generate_configs() {
200   global $amp_conf;
201   global $dryrun;
202   global $debug;
203  
204   out("Generating Configurations.conf, (if Asterisk is not running, you will get an error");
205   out("In case of error, start Asterisk and hit the red bar in the GUI to generate the Configurations.conf files");
206   if (!$dryrun)
207     // added --run-install to make it work like it has been working since retrieve_conf changed to not run module install scripts by default
208
209     //
210     // TODO: Should check if Asterisk is running and/or try to start it.
211     // TODO: test this
212     //
213     $asteriskuser = isset($amp_conf['AMPASTERISKUSER']) && $amp_conf['AMPASTERISKUSER'] ? $amp_conf['AMPASTERISKUSER'] : 'asterisk';
214     passthru("sudo -u $asteriskuser  ".trim($amp_conf["AMPBIN"])."/retrieve_conf --run-install ".($debug ? ' --debug' : ''));
215 }
216
217
218 /** Collect AMP settings
219  */
220 function collect_settings($filename, $dbhost = '', $dbuser = '', $dbpass = '', $dbname = 'asterisk') {
221   global $webroot;
222   global $fopwebroot;
223   global $ampsbin_dir;
224   global $ampbin_dir;
225   global $asterisk_user;
226   global $asterisk_pass;
227
228   out("Creating new $filename");
229  
230   outn("Enter your USERNAME to connect to the '$dbname' database:\n [".($dbuser ? $dbuser : $asterisk_user) . "] ");
231   $key = trim(fgets(STDIN,1024));
232   if (preg_match('/^$/',$key))
233     $amp_conf["AMPDBUSER"] = ($dbuser ? $dbuser : $asterisk_user);
234   else
235     $amp_conf["AMPDBUSER"] = $key;
236  
237   outn("Enter your PASSWORD to connect to the '$dbname' database:\n [".($dbpass ? $dbpass : $asterisk_pass)."] ");
238   $key = trim(fgets(STDIN,1024));
239   if (preg_match('/^$/',$key))
240     $amp_conf["AMPDBPASS"] = ($dbpass ? $dbpass : $asterisk_pass);
241   else
242     $amp_conf["AMPDBPASS"] = $key;
243  
244   outn("Enter the hostname of the '$dbname' database:\n [".($dbhost ? $dbhost : "localhost")."] ");
245   $key = trim(fgets(STDIN,1024));
246   if (preg_match('/^$/',$key))
247     $amp_conf["AMPDBHOST"] = ($dbhost ? $dbhost : "localhost");
248   else
249     $amp_conf["AMPDBHOST"] = $key;
250  
251   outn("Enter a USERNAME to connect to the Asterisk Manager interface:\n [admin] ");
252   $key = trim(fgets(STDIN,1024));
253   if (preg_match('/^$/',$key)) $amp_conf["AMPMGRUSER"] = "admin";
254   else $amp_conf["AMPMGRUSER"] = $key;
255  
256   outn("Enter a PASSWORD to connect to the Asterisk Manager interface:\n [amp111] ");
257   $key = trim(fgets(STDIN,1024));
258   if (preg_match('/^$/',$key)) $amp_conf["AMPMGRPASS"] = "amp111";
259   else $amp_conf["AMPMGRPASS"] = $key;
260  
261   do {
262     out("Enter the path to use for your AMP web root:\n [$webroot] ");
263     $key = trim(fgets(STDIN,1024));
264     if (preg_match('/^$/',$key))
265       $amp_conf["AMPWEBROOT"] = "$webroot";
266     else
267       $amp_conf["AMPWEBROOT"] = rtrim($key,'/');
268
269     if (is_dir($amp_conf["AMPWEBROOT"])) {
270       break;
271     } else if (amp_mkdir($amp_conf["AMPWEBROOT"],"0755",true)){
272       out("Created ".$amp_conf["AMPWEBROOT"]);
273       break;
274     } else {
275       fatal("Cannot create ".$amp_conf["AMPWEBROOT"]."!");
276     }
277   } while(1);
278  
279   // Really no need to ask, is there.
280   if (empty($fopwebroot))
281     $amp_conf["FOPWEBROOT"] = $amp_conf["AMPWEBROOT"]."/panel";
282   else
283     $amp_conf["FOPWEBROOT"] = $fopwebroot;
284  
285   outn("Enter the IP ADDRESS or hostname used to access the AMP web-admin:\n [xx.xx.xx.xx] ");
286   $key = trim(fgets(STDIN,1024));
287   if (preg_match('/^$/',$key)) $amp_conf["AMPWEBADDRESS"] = "xx.xx.xx.xx";
288   else $amp_conf["AMPWEBADDRESS"] = $key;
289  
290   outn("Enter a PASSWORD to perform call transfers with the Flash Operator Panel:\n [passw0rd] ");
291   $key = trim(fgets(STDIN,1024));
292   if (preg_match('/^$/',$key)) $amp_conf["FOPPASSWORD"] = "passw0rd";
293   else $amp_conf["FOPPASSWORD"] = $key;
294  
295   outn("Use simple Extensions [extensions] admin or separate Devices and Users [deviceanduser]?\n [extensions] ");
296   $key = trim(fgets(STDIN,1024));
297   if (preg_match('/^$/',$key)) $amp_conf["AMPEXTENSIONS"] = "extensions";
298   else $amp_conf["AMPEXTENSIONS"] = $key;
299  
300   do {
301     out("Enter directory in which to store AMP executable scripts:\n [$ampbin_dir] ");
302     $key = trim(fgets(STDIN,1024));
303     if (preg_match('/^$/',$key))
304       $amp_conf["AMPBIN"] = $ampbin_dir;
305     else
306       $amp_conf["AMPBIN"] = rtrim($key,'/');
307
308     if (is_dir($amp_conf["AMPBIN"])) {
309       break;
310     } else if (amp_mkdir($amp_conf["AMPBIN"],"0755",true)){
311       out("Created ".$amp_conf["AMPBIN"]);
312       break;
313     } else {
314       fatal("Cannot create ".$amp_conf["AMPBIN"]."!");
315     }
316   } while(1);
317  
318   do {
319     out("Enter directory in which to store super-user scripts:\n [$ampsbin_dir] ");
320     $key = trim(fgets(STDIN,1024));
321     if (preg_match('/^$/',$key))
322       $amp_conf["AMPSBIN"] = "$ampsbin_dir";
323     else
324       $amp_conf["AMPSBIN"] = rtrim($key,'/');
325
326     if (is_dir($amp_conf["AMPSBIN"])) {
327       break;
328     } else if (amp_mkdir($amp_conf["AMPSBIN"],"0755",true)){
329       out("Created ".$amp_conf["AMPSBIN"]);
330       break;
331     } else {
332       fatal("Cannot create ".$amp_conf["AMPSBIN"]."!");
333     }
334   } while(1);
335  
336   // write amportal.conf
337   write_amportal_conf($filename, $amp_conf);
338   outn(AMP_CONF." written");
339 }
340
341 /** Set base of packaged modules to the versions packaged in the tarball since they are
342  *  getting overwritten with the tarball from anything that may have been updated online.
343  *
344  */
345 function set_base_version() {
346   global $dryrun;
347
348   // read modules list from MODULE_DIR
349   //
350   $included_modules = array();
351   $dir = opendir(MODULE_DIR);
352   while ($file = readdir($dir)) {
353     if ($file[0] != "." && $file[0] != "_" && is_dir(MODULE_DIR."/".$file)) {
354       $included_modules[] = $file;
355     }
356   }
357   closedir($dir);
358
359   foreach ($included_modules as $up_module) {
360     outn("Checking $up_module.. ");
361     if (!$dryrun) {
362       out(set_module_version($up_module));
363     } else {
364       out("Dry Run Not Updated");
365     }
366   }
367 }
368
369 /** Install all modules packaged with the install. We use the force flag because the
370  *  the assumption is that the dependencies are met and the package is able to have
371  *  the modules installed.
372  */
373 function install_modules() {
374   global $dryrun;
375   global $amp_conf;
376
377   // read modules list from MODULE_DIR
378   //
379   $included_modules = array();
380   $dir = opendir(MODULE_DIR);
381   while ($file = readdir($dir)) {
382     if ($file[0] != "." && $file[0] != "_" && is_dir(MODULE_DIR."/".$file)) {
383       $included_modules[] = $file;
384     }
385   }
386   closedir($dir);
387
388   $output = array();
389   $keep_checking = true;
390   $num_modules = false;
391   while ($keep_checking && count($included_modules)) {
392     if ($num_modules === count($included_modules)) {
393       $keep_checking = false;
394     } else {
395       $num_modules = count($included_modules);
396     }
397     foreach ($included_modules as $id => $up_module) {
398       outn("Checking $up_module.. ");
399       if (!$dryrun) {
400         // if $keep_checking then check dependencies first and skip if not met
401         // otherwise we will install anyhow even if some dependencies are not met
402         // since it is included. (TODO: should we not?)
403         //
404         if ($keep_checking) {
405           exec($amp_conf['AMPBIN']."/module_admin --no-warnings checkdepends $up_module",$output,$retval);
406           unset($output);
407           if ($retval) {
408             out("dependencies pending");
409             continue;
410           }
411         }
412
413         // special case framework, it should not be installed just enabled.
414         //
415         if ($up_module != 'framework') {
416           system($amp_conf['AMPBIN']."/module_admin --no-warnings -f install $up_module");
417           system($amp_conf['AMPBIN']."/module_admin --no-warnings -f enable $up_module");
418           out("installed");
419         } else {
420           system($amp_conf['AMPBIN']."/module_admin --no-warnings -f enable $up_module");
421           out("enabled");
422         }
423         unset($included_modules[$id]);
424       } else {
425         out("Dry Run Not Installed");
426       }
427     }
428   }
429 }
430
431 /** Set the module version number to the packaged version and enable
432  *  module must require not install.php or install.sql script
433  *  this is primarily to package core and framework with FreePBX tarballs
434  *
435  */
436 function set_module_version($module) {
437   global $db;
438
439   $module_dir = MODULE_DIR;
440   $file_path = $module_dir.$module."/module.xml";
441   if (file_exists($file_path)) {
442     // TODO: this is bad, there are other version tags (depends on) but this
443     //       is equivalnet to what publish.pl does, so it expects this to be
444     //       at the top.
445     //
446     $module_xml = file_get_contents($file_path);
447     if (preg_match('/<version>(.+)<\/version>/', $module_xml, $matches)) {
448       $version = $matches[1];
449     } else {
450       fatal("ERROR: $file_path found but no version information");
451     }
452   } else {
453     return  "not packaged, no updating needed";
454   }
455
456   // If we didn't return above, then we found the package as part of the install
457   // tarball and want to update the version info since this might be overwriting
458   // an existing install that has a newer version.
459   //
460   $sql = "SELECT version FROM modules WHERE modulename = '$module'";
461   $result = $db->getCol($sql);
462   if(DB::IsError($result)) {     
463     fatal("error accessing version table: ".$result->getMessage());
464   }
465   $sql = "";
466   if (count($result) == 0) {
467     // insert but disable as we have to first run install scripts which come later
468     $sql = "INSERT INTO modules (modulename, version, enabled) VALUES ('$module', '$version', 0)";
469   } else if ($result[0] != $version) {
470     if (version_compare_freepbx($version, $result[0], "gt")) {
471       // if new version is greater than old, then we disable the module and it will get enabled next when installed
472       //
473       $sql = "UPDATE modules SET version = '$version', enabled = 0 WHERE modulename = '$module'";
474     } else {
475       // if new version is equal to or less than old, then we leave it in the enable/disable state it was in but just
476       // reset the version number.
477       //
478       $sql = "UPDATE modules SET version = '$version' WHERE modulename = '$module'";
479     }
480   }
481   if ($sql) {
482     debug($sql);
483     $result = $db->query($sql);
484     if(DB::IsError($result)) {     
485       fatal("error writing to version table: ".$result->getMessage());
486     }
487     return "updated to $version";
488   } else {
489     return "already at $version";
490   }
491 }
492
493 /********************************************************************************************************************/
494
495 //
496 // TODO: will this work, basically using bootstrap once installed if using to upgrade, etc?
497 /*
498 $bootstrap_settings['skip_astman']) = true;
499 $bootstrap_settings['freepbx_auth'] = false;
500 $restrict_mods = true;
501 if (@include_once(getenv('FREEPBX_CONF') ? getenv('FREEPBX_CONF') : '/etc/freepbx.conf')) {
502   out(_("FreePBX already installed, bootstrapping"));
503 } else if (@include_once('/etc/asterisk/freepbx.conf')) {
504   out(_("FreePBX already installed, bootstrapping"));
505 } else {
506   // all the initialization here instead
507 }
508 */
509
510 // **** Make sure we have STDIN etc
511
512 // from  ben-php dot net at efros dot com   at  php.net/install.unix.commandline
513 if (version_compare(phpversion(),'4.3.0','<') || !defined("STDIN")) {
514   define('STDIN',fopen("php://stdin","r"));
515   define('STDOUT',fopen("php://stdout","r"));
516   define('STDERR',fopen("php://stderr","r"));
517   register_shutdown_function( create_function( '' , 'fclose(STDIN); fclose(STDOUT); fclose(STDERR); return true;' ) );
518 }
519    
520 // **** Make sure we have PEAR's DB.php, and include it
521
522 outn("Checking for PEAR DB..");
523 if (! @ include('DB.php')) {
524   out("FAILED");
525   fatal("PEAR must be installed (requires DB.php). Include path: ".ini_get("include_path"));
526 }
527 out("OK");
528
529 // **** Make sure we have PEAR's GetOpts.php, and include it
530
531 outn("Checking for PEAR Console::Getopt..");
532 if (! @ include("Console/Getopt.php")) {
533   out("FAILED");
534   fatal("PEAR must be installed (requires Console/Getopt.php). Include path: ".ini_get("include_path"));
535 }
536 out("OK");
537
538 // **** Parse out command-line options
539 $shortopts = "h?u:p:";
540 $longopts = array("help","debug","dry-run","username=","password=","force-version=","dbhost=","no-files","force-overwrite","dbname=","my-svn-is-correct","engine=","webroot=","install-moh","install-fop=","make-links-devel","dev-links","skip-module-install");
541
542 $args = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), $shortopts, $longopts);
543 if (is_object($args)) {
544   // assume it's PEAR_ERROR
545   out($args->message);
546   exit(255);
547 }
548
549 $debug = false;
550 $dryrun = false;
551 $install_files = true;
552 $override_astvers = false;
553
554 $install_moh = false;
555 $install_fop = true;
556 $make_links = false;
557 $module_install = true;
558
559 //initialize variables to avoid php notices
560 $dbhost = null;
561 $dbname = null;
562 $new_username = null;
563 $new_password = null;
564
565 foreach ($args[0] as $arg) {
566   switch ($arg[0]) {
567     case "--help": case "h": case "?":
568       showHelp();
569       exit(10);
570     break;
571     case "--dry-run":
572       out("Dry-run only, nothing will be changed");
573       $dryrun = true;
574     break;
575     case "--debug":
576       $debug = true;
577       debug("Debug mode enabled");
578     break;
579     case "--username": case "u":
580       out("Using username: ".$arg[1]);
581       $new_username = $arg[1];
582     break;
583     case "--password": case "p":
584       out("Using password: ".str_repeat("*",strlen($arg[1])));
585       $new_password = $arg[1];
586     break;
587     case "--force-version":
588       $version = $arg[1];
589       out("Forcing upgrade from version ".$version);
590     break;
591     case "--dbhost":
592       $dbhost = $arg[1];
593       out("Using remote database server at ".$dbhost);
594     break;
595     case "--dbname":
596       $dbname = $arg[1];
597       out("Using database ".$dbname);
598     break;
599     case "--no-files":
600       $install_files = false;
601       out("Running upgrade only, without installing files.");
602     break;
603     case "--force-overwrite":
604       $check_md5s = false;
605       out("Overwriting all files including modified ones.");
606     break;
607     case "--my-svn-is-correct":
608       $override_astvers = true;
609     break;
610     case "--engine":
611       if ($arg[1] != 'asterisk') {
612         fatal('Currently only "asterisk" is supported as a PBX engine');
613       }
614       $pbx_engine = $arg[1];
615     break;
616     case "--install-moh":
617       $install_moh = true;
618     break;
619     case "--install-fop":
620       if(strtolower($arg[1]) == "false")  {
621         $install_fop = false;
622         out("FOP will be deactivated in the interface.  Set FOPDISABLE=false in Advanced Settings to change later.");
623       } else  {
624         out("FOP will be installed and activated.");
625       }
626     break;
627     case "--make-links-devel":
628     case "--dev-links":
629       $make_links = true;
630     break;
631     case "--skip-module-install":
632       $module_install = false;
633     break;
634     case "--fopwebroot":
635       $fopwebroot = $arg[1];
636       out("Using fop at ".$fopwebroot);
637     break;
638     case "--webroot":
639       $webroot = $arg[1];
640       out("Using Webroot at ".$webroot);
641     break;
642     case "--cgibin":
643       $cgibin = $arg[1];
644       out("Using CGI-BIN at ".$cgibin);
645     break;
646     case "--bin":
647       $ampbin_dir = $arg[1];
648       out("Using bin at ".$ampbin_dir);
649     break;
650     case "--sbin":
651       $ampsbin_dir = $arg[1];
652       out("Using sbin ar ".$ampsbin_dir);
653     break;
654     case "--asteriskuser":
655       $asterisk_user = $arg[1];
656       out("Using Asterisk user ".$asterisk_user);
657     break;
658     case "--asteriskpass":
659       $asterisk_pass = $arg[1];
660       out("Using asteriskpass ".str_repeat("*",strlen($arg[1])));
661     break;
662
663 /*    do we need this ?
664     case "--systemconfig":
665       $systemconfig = $arg[1];
666       out("Using system config at ". $systemconfig);
667     break;
668 */
669
670   }
671 }
672
673
674 // **** Look for user = root
675
676 // TODO: if we bootstrap, do this first before even trying. Also, is their a problem boostraping as user root???
677 //
678 outn("Checking user..");
679 //$current_user=(isset($_ENV["USER"]) ? $_ENV["USER"] : exec('whoami',$output));
680 $euid = (posix_getpwuid(posix_geteuid()));
681 $current_user = $euid['name'];
682 if ($current_user != "root"){
683   out("FAILED");
684   fatal($argv[0]." must be run as root");
685 }
686 out("OK");
687
688
689 // TODO: if we bootstrap, do this first before even trying.
690 //
691 outn("Checking if Asterisk is running..");
692 exec("pidof asterisk", $pid_val, $ret);
693 if ($ret) {
694   out("FAILED");
695   fatal($argv[0]."\n\tAsterisk must be running. If this is a first time install, you should start\n\tAsterisk by typing './start_asterisk start'\n\tFor upgrading, you should run 'amportal start'");
696 }
697 out("running with PID: ".$pid_val[0]."..OK");
698
699 // **** Check for amportal.conf, create if necessary
700
701 outn("Checking for ".AMP_CONF."..");
702 if (!file_exists(AMP_CONF)) {
703   out(AMP_CONF." does not exist, copying default");
704   copy("amportal.conf", AMP_CONF);
705
706   // this file contains password and should not be a+r
707   // this addresses http://freepbx.org/trac/ticket/1878
708   chown(AMP_CONF, "asterisk");
709   chgrp(AMP_CONF, "asterisk");
710   chmod(AMP_CONF, 0640);
711
712   collect_settings(AMP_CONF, $dbhost, $new_username, $new_password, 'asterisk');
713
714   out("Assuming new install, --install-moh added to command line");
715   $install_moh = true;
716 }
717 out("OK");
718
719 // **** read amportal.conf
720
721 // TODO: see comments above, have we already boostraped, if so, skip this?
722 //
723 outn("Reading ".AMP_CONF."..");
724 $amp_conf = install_parse_amportal_conf(AMP_CONF);
725 if (count($amp_conf) == 0) {
726   fatal("FAILED");
727 }
728 out("OK");
729
730 // TODO: if we boostrapped I think we can count on ALL of these being set but maybe it's
731 //       fine to do a sanity check?
732 //
733 // Ensure our "critical" variables are set.  We absolutely need these to copy in files.
734
735 if (!array_key_exists("AMPWEBROOT",$amp_conf)) {
736   out("Adding AMPWEBROOT option to amportal.conf - using AMP default");
737   $amp_conf["AMPWEBROOT"] = "/var/www/html";
738 }
739 if (!array_key_exists("FOPWEBROOT",$amp_conf)) {
740   out("Adding FOPWEBROOT option to amportal.conf - using AMP default");
741   $amp_conf["FOPWEBROOT"] = $amp_conf["AMPWEBROOT"]."/panel";
742 }
743 if (!array_key_exists("AMPBIN",$amp_conf)) {
744   out("Adding AMPBIN option to amportal.conf - using AMP default");
745   $amp_conf["AMPBIN"] = "/var/lib/asterisk/bin";
746 }
747 if (!array_key_exists("AMPSBIN",$amp_conf)) {
748   out("Adding AMPSBIN option to amportal.conf - using AMP default");
749   $amp_conf["AMPSBIN"] = "/usr/sbin";
750 }
751 if (!array_key_exists("AMPDBENGINE",$amp_conf)) {
752   out("Adding AMPDBENGINE option to amportal.conf - using AMP default");
753   $amp_conf["AMPDBENGINE"] = "mysql";
754 }
755 if (!array_key_exists("AMPDBNAME",$amp_conf)) {
756   out("Adding AMPDBNAME option to amportal.conf - using AMP default");
757   $amp_conf["AMPDBNAME"] = "asterisk";
758 }
759
760 // TODO: if we boostrapped do we ignore these? (Can they even be set?)
761 //
762 if (isset($new_username)) {
763   $amp_conf["AMPDBUSER"] = $new_username;
764 }
765 if (isset($new_password)) {
766   $amp_conf["AMPDBPASS"] = $new_password;
767 }
768 if (isset($dbhost)) {
769   $amp_conf["AMPDBHOST"] = $dbhost;
770 }
771 if (isset($dbname)) {
772   $amp_conf["AMPDBNAME"] = $dbname;
773 }
774 if(!$install_fop)  { // Set from --install-fop parameter; Add it to amportal.conf
775   out("Adding FOPDISABLE option to amportal.conf");
776   $amp_conf["FOPDISABLE"] = "true";
777 }
778 // If they pre-set this in their amportal.conf or this is an upgrade, we should honor it as well
779 //
780 // TODO: if we boostrap, then this value could be normalized (1/0 I think?)
781 //
782 if (isset($amp_conf['FOPDISABLE']) && (strtolower(trim($amp_conf['FOPDISABLE'])) == 'true' || strtolower(trim($amp_conf['FOPDISABLE'])) == 'yes' || strtolower(trim($amp_conf['FOPDISABLE'])) == 'y') )  {
783   $install_fop = false;
784 }
785 // write amportal.conf
786 // TODO: if we boostrapped we don't want to write amportal.conf, or do we? e.g. if we are running out of the file vs. the
787 //       database then we probably want to write, though there should be a freepbx_conf class to use that does that and knows
788 //       how to make that decsion as well as what to write?
789 //
790 write_amportal_conf(AMP_CONF, $amp_conf);
791
792 // **** Check for amportal.conf, create if necessary
793
794 outn("Checking for ".ASTERISK_CONF."..");
795 if (!file_exists(ASTERISK_CONF)) {
796   out(ASTERISK_CONF." does not exist, copying default");
797   copy("asterisk.conf", ASTERISK_CONF);
798 }
799 out("OK");
800
801 // **** read asterisk.conf
802
803 // TODO: if we boostrapped don't do this
804 //
805 outn("Reading ".ASTERISK_CONF."..");
806 $asterisk_conf = install_parse_asterisk_conf(ASTERISK_CONF);
807 if (count($asterisk_conf) == 0) {
808   fatal("FAILED. Have you installed Asterisk?");
809 }
810 out("OK");
811
812 /* TODO: if we boostrapped this should have been done but doesn't hurt?
813  */
814 if (isset($asterisk_conf['astetcdir'])) { $amp_conf['ASTETCDIR'] = $asterisk_conf['astetcdir']; }
815 if (isset($asterisk_conf['astmoddir'])) { $amp_conf['ASTMODDIR'] = $asterisk_conf['astmoddir']; }
816 if (isset($asterisk_conf['astvarlibdir'])) { $amp_conf['ASTVARLIBDIR'] = $asterisk_conf['astvarlibdir']; }
817 if (isset($asterisk_conf['astagidir'])) { $amp_conf['ASTAGIDIR'] = $asterisk_conf['astagidir']; }
818 if (isset($asterisk_conf['astspooldir'])) { $amp_conf['ASTSPOOLDIR'] = $asterisk_conf['astspooldir']; }
819 if (isset($asterisk_conf['astrundir'])) { $amp_conf['ASTRUNDIR'] = $asterisk_conf['astrundir']; }
820 if (isset($asterisk_conf['astlogdir'])) { $amp_conf['ASTLOGDIR'] = $asterisk_conf['astlogdir']; }
821
822 if (!isset($pbx_engine)) { $pbx_engine='asterisk'; }
823 out("Using $pbx_engine as PBX Engine");
824 $amp_conf["AMPENGINE"]=$pbx_engine;
825
826 // TODO: I guess we have new variables set, of course see note above if we boostrapped. Probably
827 //       this one stays?
828 //
829 write_amportal_conf(AMP_CONF, $amp_conf);
830
831
832 // **** Write asterisk version to ASTETCDIR/version
833
834 // TODO: does anything use this? What's the point? (other than we need the version for below)
835 //
836 $tmpoutput = '';
837 $tmpout = exec("asterisk -V", $tmpoutput, $exitcode);
838 if ($exitcode != 0) {
839   fatal("Error executing asterisk: be sure Asterisk is installed and in the path");
840 }
841 if (!$fd = fopen($amp_conf['ASTETCDIR'].'/version','w')) {
842   fatal('Cannot open '.$amp_conf['ASTETCDIR'].'/version for writing');
843 }
844 fwrite($fd, $tmpout);
845 fclose($fd);
846 // change to read-only
847 chmod($amp_conf['ASTETCDIR'].'/version',0444);
848
849
850 // normally this would be the contents of ASTETCDIR/version, but this is for simplicity, as we just read it above
851 $verinfo = $tmpout;
852
853 // **** Check asterisk version
854 //  Set the 'engine' to be 'asterisk14' if using asterisk 1.4, otherwise
855 //  'asterisk'
856 outn("Checking for Asterisk version..");
857 if ((preg_match('/^Asterisk (\d+(\.\d+)*)(-?(.*))$/', $verinfo, $matches)) ||
858     (preg_match('/^Asterisk SVN-(\d+(\.\d+)*)(-?(.*))$/', $verinfo, $matches))) {
859   if ((version_compare($matches[1], "1.2") < 0)) {
860     fatal("Asterisk 1.2, 1.4, 1.6, or 1.8 is required for this version of FreePBX. Detected version is: ".$matches[1]);
861   }
862   if (version_compare($matches[1], "1.9", "ge")) {
863     fatal("Asterisk 1.2, 1.4, 1.6, or 1.8 is required for this version of FreePBX. Detected version is: ".$matches[1]);
864   }
865   out("{$matches[1]}");
866   $asterisk_version = $matches[1];
867
868 } elseif (preg_match('/^Asterisk ([ABC]\.\d+(\.\d+)*)(-?(.*))$/', $verinfo, $matches)) {
869   if (substr($matches[1], 0, 1) == "A") {
870     fatal("Asterisk Business Edition B or C is required for this version of FreePBX. Detected version is: ".$matches[1]);
871   }
872   out("{$matches[1]}");
873   $asterisk_version = '1.4'; //Not really, but this makes sure that proper execution occurs later on reload commands, etc.
874
875 } elseif ($asterisk_version = preg_match('/^Asterisk SVN.+/', $verinfo)) {
876   out("FAIL");
877   out("*** WARNING ***");
878   out("You are not using a released version of Asterisk. We are unable to verify");
879   out("that your Asterisk version is compatible with FreePBX. Whilst this probably");
880   out("won't cause any problems, YOU NEED TO BE CERTAIN that it is compatible");
881   out("with at least the released Asterisk version 1.2" );
882   if ($override_astvers==false) {
883     out("If you are SURE that this is compatible, you can re-run ".$argv[0]." with");
884     out("the parameter --my-svn-is-correct");
885     exit;
886   } else {
887     out("--my-svn-is-correct specified, continuing");
888   }
889 } else {
890   fatal("Could not determine asterisk version (got: \"".$verinfo."\" please report this)");
891 }
892
893 // **** Make sure selinux isn't enabled
894
895 outn("Checking for selinux..");
896 $tmpoutput;
897 $tmpout = exec("getenforce", $tmpoutput, $sereturn);
898 if (strtolower($tmpoutput[0]) === "enabled") {
899         // this method seems better because disabled and permissive are the same
900         // if a user installs and realizes selinux is running the other method
901         // requires a reboot to get selinuxenabled to work after editing the  selinux config
902         // this will allow you to use setenforce 0 which turns selinux into permissive mode which
903         // doesnt enforce, it just warns.
904   fatal("selinux is ENABLED. This is not supported. Please disable selinux before using FreePBX");
905 }
906 out("OK");
907
908 // **** Connect to database
909
910 // TODO: if we bootstrapped, we are already connected and this can all be skipped
911 //
912 outn("Connecting to database..");
913
914 $db_engine = $amp_conf["AMPDBENGINE"];
915 if ($db_engine != "sqlite3") {
916   $db_user = $amp_conf["AMPDBUSER"];
917   $db_pass = $amp_conf["AMPDBPASS"];
918   $db_host = $amp_conf["AMPDBHOST"];
919 }
920 $db_name = $amp_conf["AMPDBNAME"];
921
922 // we still support older configurations,  and fall back
923 // into mysql when no other engine is defined
924 if ($db_engine == "")
925 {
926   $db_engine = "mysql";
927 }
928  
929 switch ($db_engine)
930 {
931   case "pgsql":
932   case "mysql":
933     // datasource in in this style: dbengine://username:password@host/database
934     if (!function_exists($db_engine.'_connect')) {
935       out("FAILED");
936       fatal($db_engine." PHP libraries not installed");
937     }
938  
939     $datasource = $db_engine.'://'.$db_user.':'.$db_pass.'@'.$db_host.'/'.$db_name;
940     $db = DB::connect($datasource); // attempt connection
941     break;
942  
943   case "sqlite":
944     die_freepbx("SQLite2 support is deprecated. Please use sqlite3 only.");
945     break;
946  
947   case "sqlite3":
948     if (!isset($amp_conf["AMPDBFILE"]))
949       die("You must setup properly AMPDBFILE in /etc/amportal.conf");
950      
951     if (isset($amp_conf["AMPDBFILE"]) == "")
952       die("AMPDBFILE in /etc/amportal.conf cannot be blank");
953
954     /* on centos this extension is not loaded by default */
955     if (! extension_loaded('sqlite3.so')  && ! extension_loaded('SQLITE3'))
956       dl('sqlite3.so');
957
958     if (! @require_once('DB/sqlite3.php') )
959     {
960       out("FAILED");
961       fatal( "Your PHP installation has no PEAR/SQLite3 support. Please install php-sqlite3 and php-pear.");
962     }
963
964     $datasource = "sqlite3:///" . $amp_conf["AMPDBFILE"] . "?mode=0666";
965     $db = DB::connect($datasource);
966     break;
967
968   default:
969     die( "Unknown SQL engine: [$db_engine]");
970 }
971
972 if(DB::IsError($db)) {
973   out("FAILED");
974   debug($db->userinfo);
975   out("Try running ".$argv[0]." --username=user --password=pass  (using your own user and pass)");
976   fatal("Cannot connect to database");
977  
978 }
979 out("OK");
980
981
982 // **** Read DB for version info
983
984 if (!isset($version)) {
985   outn("Checking current version of AMP..");
986   $version = install_getversion();
987   if (!$version) {
988     out("no version information");
989     out("Assuming new installation");
990   } else {
991     out($version);
992   }
993 }
994
995 // Check if freepbx_settings is there and has been initialized
996 // if it has anything less than about 5 settings in it (and that
997 // is probably quite low) then something is wrong so assume it
998 // is not there.
999 //
1000 $sql = "SELECT count(*) FROM freepbx_settings";
1001 $num_settings = $db->getOne($sql);
1002 $force_amportal_conf = DB::IsError($num_settings) || $num_settings < 5;
1003
1004 // **** Copy files
1005
1006 if ($install_files)
1007 {
1008   outn("Installing new FreePBX files..");
1009   $check_md5s=true;
1010   $md5sums = read_md5_file(UPGRADE_DIR."/".$version.".md5");
1011   list($num_files, $num_copied) = recursive_copy("amp_conf", "", $md5sums);
1012   if (!is_file("/etc/asterisk/voicemail.conf")) copy("/etc/asterisk/voicemail.conf.template","/etc/asterisk/voicemail.conf");
1013   if (!is_dir("/var/spool/asterisk/voicemail/device")) amp_mkdir("/var/spool/asterisk/voicemail/device", "0755", true);
1014   out("OK (".$num_copied." files copied, ".($num_files-$num_copied)." skipped)");
1015
1016   // link the packed js library for ARI, if there the error will indicate that
1017   $libfreepbx = $amp_conf['AMPWEBROOT'].'/admin/common/libfreepbx.javascripts.js';
1018   $dest_libfreepbx = $amp_conf['AMPWEBROOT'].'/recordings/theme/js/libfreepbx.javascripts.js';
1019   if (file_exists($libfreepbx) && !file_exists($dest_libfreepbx)) {
1020     outn(_("linking libfreepbx.javascripts.js to theme/js.."));
1021     if (link($libfreepbx, $dest_libfreepbx)) {
1022       out(_("ok"));
1023     } else {
1024       out(_("possible error - check warning message"));
1025     }
1026   }
1027 }
1028
1029 // **** Apply amportal.conf configuration to files
1030 if (file_exists(dirname(__FILE__)."/apply_conf.sh")) {
1031   debug("Running ".dirname(__FILE__)."/apply_conf.sh");
1032 }
1033
1034 outn("Configuring install for your environment..");
1035 if (!$dryrun) {
1036   $asteriskuser = isset($amp_conf['AMPASTERISKUSER']) && $amp_conf['AMPASTERISKUSER'] ? $amp_conf['AMPASTERISKUSER'] : 'asterisk';
1037
1038   if (file_exists($amp_conf["AMPSBIN"]."/amportal")) {
1039     exec("chmod u+x ".$amp_conf["AMPSBIN"]."/amportal");
1040     outn("amportal..");
1041   } else {
1042     outn("no amportal..");
1043   }
1044   if (file_exists($amp_conf["AMPSBIN"]."/fpbx")) {
1045     exec("chmod u+x ".$amp_conf["AMPSBIN"]."/fpbx");
1046     outn("fpbx..");
1047   } else {
1048     outn("no fpbx..");
1049   }
1050   if (file_exists($amp_conf["AMPBIN"]."/freepbx_engine")) {
1051     exec("chmod u+x ".$amp_conf["AMPBIN"]."/freepbx_engine");
1052     outn("freepbx_engine..");
1053   } else {
1054     outn("no freepbx_engine..");
1055   }
1056   out("done");
1057   // edit conf file passwords and then
1058   // reload manager in asterisk if it is running:
1059   //
1060   outn("apply username/password changes to conf files..");
1061   if (file_exists(dirname(__FILE__)."/apply_conf.sh")) {
1062     if ($force_amportal_conf) {
1063       putenv('FORCE_AMPORTAL_CONF=1');
1064       exec(dirname(__FILE__)."/apply_conf.sh ".AMP_CONF);
1065       putenv('FORCE_AMPORTAL_CONF=');
1066     } else {
1067       exec(dirname(__FILE__)."/apply_conf.sh");
1068     }
1069   }
1070   out("done");
1071
1072   /* As of Asterisk 1.4.16 or there about, a missing #include file will make the reload fail. So
1073     we need to make sure that we have such for everything that is in our configs. We will simply
1074     look for the #include statements and touch the files vs. trying to inventory everything we may
1075     need and then forgetting something.
1076   */
1077  
1078   outn("creating missing #include files..");
1079   $include_err = false;
1080   exec("grep '#include' ".$amp_conf['ASTETCDIR']."/*.conf | sed 's/;.*//; s/#include//'",$output,$retcode);
1081   if ($retcode != 0) {
1082     out("Error code $retcode: trying to search for missing #include files");
1083     $include_err = true;
1084   }
1085
1086   foreach($output as $file) {
1087     if (trim($file) == '') {
1088       continue;
1089     }
1090     $parse1 = explode(':',$file);
1091     $parse2 = explode(';',$parse1[1]);
1092     $rawfile = trim($parse2[0]);
1093     if ($rawfile == '') {
1094       continue;
1095     }
1096
1097     $target = ($rawfile[0] == '/') ? $rawfile : $amp_conf['ASTETCDIR']."/$rawfile";
1098
1099     if (!file_exists($target)) {
1100       exec("sudo -u $asteriskuser touch $target", $output, $retcode);
1101         if ($retcode != 0) {
1102         out("Error code $retcode: trying to create empty file $target");
1103         $include_err = true;
1104       }
1105     }
1106   }
1107   if (! $include_err) {
1108     out("OK");
1109   }
1110   // reload manager in asterisk if it was running:
1111   //
1112   if (version_compare($asterisk_version,'1.4','ge')) {
1113     system("asterisk -rx 'module reload manager'");
1114   } else {
1115     system("asterisk -rx 'reload manager'"); //1.2 syntax
1116   }
1117 }
1118 out("OK");
1119
1120 // **** Create spool directories for monitor and fax
1121 if (!is_dir($amp_conf["ASTSPOOLDIR"]."/monitor"))
1122   amp_mkdir($amp_conf["ASTSPOOLDIR"]."/monitor","0766",true);
1123 if (!is_dir($amp_conf["ASTSPOOLDIR"]."/fax"))
1124   amp_mkdir($amp_conf["ASTSPOOLDIR"]."/fax","0766",true);
1125
1126
1127 // **** Set permissions all files
1128
1129 // tell amportal/freepbx_engine to use amportal.conf and not bootstrap
1130 // if freepbx_settings has not been setup yet or you will get an error.
1131 //
1132 if ($install_files)
1133 {
1134   outn("Setting permissions on files..");
1135   if (!$dryrun) {
1136     if ($force_amportal_conf) {
1137       putenv('FORCE_AMPORTAL_CONF=1');
1138     }
1139     exec($amp_conf["AMPSBIN"]."/amportal chown");
1140     if ($force_amportal_conf) {
1141       putenv('FORCE_AMPORTAL_CONF=');
1142     }
1143   }
1144   out("OK");
1145 }
1146
1147 // Run through all the upgrade scripts starting at the specified version
1148 //
1149 upgrade_all($version);
1150
1151 outn("Creating or updating freepbx_conf settings..");
1152 freepbx_settings_init(true);
1153 out("..OK");
1154
1155 // **** Generate AMP configs
1156 out("Generating AMP configs..");
1157 generate_configs();
1158 out("Generating AMP configs..OK");
1159
1160 $version = install_getversion();
1161
1162 if($install_fop && $amp_conf["FOPRUN"])  {
1163   // **** Bounce FOP
1164   outn("Restarting Flash Operator Panel..");
1165   exec("sudo -u $asteriskuser ".$amp_conf["AMPBIN"].'/bounce_op.sh');
1166   out("OK");
1167 }
1168
1169 // Now force an install for all modules that are packaged with the tarball
1170 // directory.
1171 //
1172 if ($module_install) {
1173   install_modules();
1174 } else {
1175   out("bypassing packaged module installation because --skip-module-install flag");
1176 }
1177
1178
1179 // **** Set reload flag for AMP admin
1180 needreload();
1181
1182 if ($amp_conf["AMPWEBADDRESS"])
1183 {
1184   out("Please update your modules and reload Asterisk by visiting http://".$amp_conf["AMPWEBADDRESS"]."/admin",false);
1185 }
1186 else
1187 {
1188   out("Please update your modules and reload Asterisk by browsing to your server.",false);
1189 }
1190
1191 out("",false);
1192 out("*************************************************************************",false);
1193 out("* Note: It's possible that if you click the red 'Update Now' bar BEFORE *",false);
1194 out("* updating your modules, your machine will start dropping calls. Ensure *",false);
1195 out("* that all modules are up to date BEFORE YOU CLICK THE RED BAR. As long *",false);
1196 out("* as this is observed, your machine will be fully functional whilst the *",false);
1197 out("* upgrade is in progress.                                               *",false);
1198 out("*************************************************************************",false);
1199 ?>
Note: See TracBrowser for help on using the browser.