root/freepbx/trunk/install_amp

Revision 10976, 39.3 kB (checked in by p_lindheimer, 2 years ago)

should avoid boostrapping if no proper freepbx_settings though not tested yet re #4720, also have amportal/freepbx_engine check for /etc/asterisk/freepbx.conf if /etc/freepbx.conf is not there

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