root/freepbx/trunk/amp_conf/htdocs/admin/functions.inc.php

Revision 2807, 67.4 kB (checked in by gregmac, 7 years ago)

Added <label> tag to destinations selects

  • Property svn:mime-type set to text/html
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php /* $id$ */
2 //Copyright (C) 2004 Coalescent Systems Inc. (info@coalescentsystems.ca)
3 //
4 //This program is free software; you can redistribute it and/or
5 //modify it under the terms of the GNU General Public License
6 //as published by the Free Software Foundation; either version 2
7 //of the License, or (at your option) any later version.
8 //
9 //This program is distributed in the hope that it will be useful,
10 //but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //GNU General Public License for more details.
13
14 require_once( (defined('AMP_BASE_INCLUDE_PATH') ? AMP_BASE_INCLUDE_PATH.'/' : '').'featurecodes.class.php');
15 require_once( (defined('AMP_BASE_INCLUDE_PATH') ? AMP_BASE_INCLUDE_PATH.'/' : '').'components.class.php');
16
17 define('MODULE_STATUS_NOTINSTALLED', 0);
18 define('MODULE_STATUS_DISABLED', 1);
19 define('MODULE_STATUS_ENABLED', 2);
20 define('MODULE_STATUS_NEEDUPGRADE', 3);
21 define('MODULE_STATUS_BROKEN', -1);
22
23 function parse_amportal_conf($filename) {
24   $file = file($filename);
25   if (is_array($file)) {
26     foreach ($file as $line) {
27       if (preg_match("/^\s*([a-zA-Z0-9_]+)=([a-zA-Z0-9 .&-@=_<>\"\']+)\s*$/",$line,$matches)) {
28         $conf[ $matches[1] ] = $matches[2];
29       }
30     }
31   } else {
32     die("<h1>Missing or unreadable config file ($filename)...cannot continue</h1>");
33   }
34  
35   if ( !isset($conf["AMPDBENGINE"]) || ($conf["AMPDBENGINE"] == "")) {
36     $conf["AMPDBENGINE"] = "mysql";
37   }
38  
39   if ( !isset($conf["AMPDBNAME"]) || ($conf["AMPDBNAME"] == "")) {
40     $conf["AMPDBNAME"] = "asterisk";
41   }
42  
43   if ( !isset($conf["AMPENGINE"]) || ($conf["AMPENGINE"] == "")) {
44     $conf["AMPENGINE"] = "asterisk";
45   }
46
47 /*     
48   if (($amp_conf["AMPDBENGINE"] == "sqlite") && (!isset($amp_conf["AMPDBENGINE"])))
49     $amp_conf["AMPDBFILE"] = "/var/lib/freepbx/freepbx.sqlite";
50 */
51  
52   return $conf;
53 }
54
55 function parse_asterisk_conf($filename) {
56   $file = file($filename);
57   foreach ($file as $line) {
58     if (preg_match("/^\s*([a-zA-Z0-9]+)\s* => \s*(.*)\s*([;#].*)?/",$line,$matches)) {
59       $conf[ $matches[1] ] = $matches[2];
60     }
61   }
62   return $conf;
63 }
64
65 function getAmpAdminUsers() {
66   global $db;
67
68   $sql = "SELECT username FROM ampusers WHERE sections='*'";
69   $results = $db->getAll($sql);
70   if(DB::IsError($results)) {
71      die($results->getMessage());
72   }
73   return $results;
74 }
75
76 function getAmpUser($username) {
77   global $db;
78  
79   $sql = "SELECT username, password, extension_low, extension_high, deptname, sections FROM ampusers WHERE username = '".addslashes($username)."'";
80   $results = $db->getAll($sql);
81   if(DB::IsError($results)) {
82      die($results->getMessage());
83   }
84  
85   if (count($results) > 0) {
86     $user = array();
87     $user["username"] = $results[0][0];
88     $user["password"] = $results[0][1];
89     $user["extension_low"] = $results[0][2];
90     $user["extension_high"] = $results[0][3];
91     $user["deptname"] = $results[0][4];
92     $user["sections"] = explode(";",$results[0][5]);
93     return $user;
94   } else {
95     return false;
96   }
97 }
98
99 class ampuser {
100   var $username;
101   var $_password;
102   var $_extension_high;
103   var $_extension_low;
104   var $_deptname;
105   var $_sections;
106  
107   function ampuser($username) {
108     $this->username = $username;
109     if ($user = getAmpUser($username)) {
110       $this->_password = $user["password"];
111       $this->_extension_high = $user["extension_high"];
112       $this->_extension_low = $user["extension_low"];
113       $this->_deptname = $user["deptname"];
114       $this->_sections = $user["sections"];
115     } else {
116       // user doesn't exist
117       $this->_password = false;
118       $this->_extension_high = "";
119       $this->_extension_low = "";
120       $this->_deptname = "";
121       $this->_sections = array();
122     }
123   }
124  
125   /** Give this user full admin access
126   */
127   function setAdmin() {
128     $this->_extension_high = "";
129     $this->_extension_low = "";
130     $this->_deptname = "";
131     $this->_sections = array("*");
132   }
133  
134   function checkPassword($password) {
135     // strict checking so false will never match
136     return ($this->_password === $password);
137   }
138  
139   function checkSection($section) {
140     // if they have * then it means all sections
141     return in_array("*", $this->_sections) || in_array($section, $this->_sections);
142   }
143 }
144
145 // returns true if extension is within allowed range
146 function checkRange($extension){
147   $low = isset($_SESSION["AMP_user"]->_extension_low)?$_SESSION["AMP_user"]->_extension_low:'';
148   $high = isset($_SESSION["AMP_user"]->_extension_high)?$_SESSION["AMP_user"]->_extension_high:'';
149  
150   if ((($extension >= $low) && ($extension <= $high)) || ($low == '' && $high == ''))
151     return true;
152   else
153     return false;
154 }
155
156 // returns true if department string matches dept for this user
157 function checkDept($dept){
158   $deptname = isset($_SESSION["AMP_user"])?$_SESSION["AMP_user"]->_deptname:null;
159  
160   if ( ($dept == null) || ($dept == $deptname) )
161     return true;
162   else
163     return false;
164 }
165
166 /* look for all modules in modules dir.
167 ** returns array:
168 ** array['module']['displayName']
169 ** array['module']['version']
170 ** array['module']['type']
171 ** array['module']['status']
172 ** array['module']['items'][array(items)]
173 ** Use find_modules() to return only specific type or status
174 */
175 function find_allmodules() {
176   global $db;
177   global $amp_conf;
178  
179   if (!is_dir($amp_conf['AMPWEBROOT'].'/admin/modules'))
180   {
181       mkdir( $amp_conf['AMPWEBROOT'].'/admin/modules' );
182       return;
183   }
184  
185   $dir = opendir($amp_conf['AMPWEBROOT'].'/admin/modules');
186   $data = "<xml>";
187   //loop through each module directory, ensure there is a module.ini file
188   while ($file = readdir($dir)) {
189     if (($file != ".") && ($file != "..") && ($file != "CVS") && ($file != ".svn") && is_dir($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file) && is_file($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file.'/module.xml')) {
190       //open module.xml and read contents
191       if(is_file($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file.'/module.xml')){
192         $data .=file_get_contents($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file.'/module.xml');
193         
194       }
195     }
196   }
197   $data .= "</xml>";
198   $parser = new xml2ModuleArray($data);
199   $xmlarray = $parser->parseModulesXML($data);
200  
201   // determine details about this module from database
202   // modulename should match the directory name
203   $sql = "SELECT * FROM modules";
204   $results = $db->getAll($sql,DB_FETCHMODE_ASSOC);
205   if(DB::IsError($results)) {
206     die($results->getMessage());
207   }
208  
209   if (is_array($results)) {
210     foreach($results as $result) {
211         /*
212         set status key based on results
213         -1=broken (in table, not not on filesystem)
214         0 or null=not installed
215         1=disabled
216         2=enabled
217         3=enabled and needs upgrade
218         */
219         if(isset($xmlarray[ $result['modulename'] ] ) && is_array($xmlarray[ $result['modulename'] ])) {
220           if ($result['enabled'] != 0) {
221             // check if file and registered versions are the same
222             // version_compare returns 0 if no difference
223             if (version_compare($result['version'],$xmlarray[ $result['modulename'] ]["version"]) === 0)
224               $xmlarray[ $result['modulename'] ]["status"] = 2;
225             else
226               $xmlarray[ $result['modulename'] ]["status"] = 3;
227           } else {
228             $xmlarray[ $result['modulename'] ]["status"] = 1;
229           }
230         } else {
231           $xmlarray[ $result['modulename'] ]["status"] = -1;
232         }
233           
234     }
235   }
236
237   //echo "<pre>"; print_r($xmlarray); echo "</pre>";
238   return $xmlarray;
239 }
240
241 /* finds modules of the specified status and type
242 ** $status can be 0 (not installed), 1 (disabled), 2 (enabled)
243 ** $type can be 'setup' or 'tool'
244 **
245 ** returns array:
246 ** array['module']['displayName']
247 ** array['module']['version']
248 ** array['module']['type']
249 ** array['module']['status']
250 ** array['module']['items'][array(items)]
251 */
252 function find_modules($status) {
253   $modules = find_allmodules();
254   if (isset($modules) && is_array($modules)) { 
255     foreach(array_keys($modules) as $key) {
256       //remove modules not matching status
257       if(isset($modules[$key]['status']) && $modules[$key]['status'] == $status ){
258         $return_modules[$key] = $modules[$key];
259       }
260     }
261     return $return_modules;
262   } else {
263     return false;
264   }
265 }
266
267
268 function engine_getinfo() {
269   global $amp_conf;
270   switch ($amp_conf['AMPENGINE']) {
271     case 'asterisk':
272       require_once('common/php-asmanager.php');
273       $astman = new AGI_AsteriskManager();
274       if ($res = $astman->connect("127.0.0.1", $amp_conf["AMPMGRUSER"] , $amp_conf["AMPMGRPASS"])) {
275         //get version
276         $response = $astman->send_request('Command', array('Command'=>'show version'));
277         $verinfo = $response['data'];
278         $astman->disconnect();
279       } else {
280         // could not connect to asterisk manager
281         return false;
282       }
283       
284       if (preg_match('/Asterisk SVN.+/', $verinfo)) {
285         return array('engine'=>'asterisk', 'version' => '99', 'additional' => '99');
286       }
287       if (preg_match('/Asterisk (\d+(\.\d+)*)(-?(\S*))/', $verinfo, $matches)) {
288         return array('engine'=>'asterisk', 'version' => $matches[1], 'additional' => $matches[4]);
289       }
290     break;
291   }
292   return false;
293 }
294
295 /* queries database using PEAR.
296 *  $type can be query, getAll, getRow, getCol, getOne, etc
297 *  $fetchmode can be DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
298 *  returns array, unless using getOne
299 */
300 function sql($sql,$type="query",$fetchmode=null) {
301   global $db;
302   $results = $db->$type($sql,$fetchmode);
303   if(DB::IsError($results)) {
304     die($results->getDebugInfo());
305   }
306   return $results;
307 }
308
309 // sql text formatting -- couldn't see that one was available already
310 function sql_formattext($txt) {
311   if (isset($txt)) {
312     $fmt = str_replace("'", "''", $txt);
313     $fmt = "'" . $fmt . "'";
314   } else {
315     $fmt = 'null';
316   }
317
318   return $fmt;
319 }
320
321 //tell application we need to reload asterisk
322 function needreload() {
323   global $db;
324   $sql = "UPDATE admin SET value = 'true' WHERE variable = 'need_reload'";
325   $result = $db->query($sql);
326   if(DB::IsError($result)) {     
327     die($result->getMessage());
328   }
329 }
330
331 //get the version number
332 function getversion() {
333   global $db;
334   $sql = "SELECT value FROM admin WHERE variable = 'version'";
335   $results = $db->getAll($sql);
336   if(DB::IsError($results)) {
337     die($results->getMessage());
338   }
339   return $results;
340 }
341
342 // draw list for users and devices with paging
343 function drawListMenu($results, $skip, $type, $dispnum, $extdisplay, $description) {
344   $perpage=20;
345  
346   $skipped = 0;
347   $index = 0;
348   if ($skip == "") $skip = 0;
349   echo "<li><a id=\"".($extdisplay=='' ? 'current':'')."\" href=\"config.php?type=".$type."&display=".$dispnum."\">"._("Add")." ".$description."</a></li>";
350
351   if (isset($results)) {
352     foreach ($results AS $key=>$result) {
353       if ($index >= $perpage) {
354         $shownext= 1;
355         break;
356       }
357       
358       if ($skipped<$skip && $skip!= 0) {
359         $skipped= $skipped + 1;
360         continue;
361       }
362       
363       $index= $index + 1;
364       echo "<li><a id=\"".($extdisplay==$result[0] ? 'current':'')."\" href=\"config.php?type=".$type."&display=".$dispnum."&extdisplay={$result[0]}&skip={$skip}\">{$result[1]} <{$result[0]}></a></li>";
365     }
366   }
367   
368   if ($index >= $perpage) {
369      print "<li><center>";
370   }
371   
372   if ($skip) {
373      $prevskip= $skip - $perpage;
374      if ($prevskip<0) $prevskip= 0;
375      $prevtag_pre= "<a href='?type=".$type."&display=".$dispnum."&skip=$prevskip'>[PREVIOUS]</a>";
376      print "$prevtag_pre";
377   }
378  
379   if (isset($shownext)) {
380     $nextskip= $skip + $index;
381     if ($prevtag_pre) $prevtag .= " | ";
382     print "$prevtag <a href='?type=".$type."&display=".$dispnum."&skip=$nextskip'>[NEXT]</a>";
383   }
384   elseif ($skip) {
385     print "$prevtag";
386   }
387   
388    print "</center></li>";
389 }
390
391 // this function simply makes a connection to the asterisk manager, and should be called by modules that require it (ie: dbput/dbget)
392 function checkAstMan() {
393   require_once('common/php-asmanager.php');
394   global $amp_conf;
395   $astman = new AGI_AsteriskManager();
396   if ($res = $astman->connect("127.0.0.1", $amp_conf["AMPMGRUSER"] , $amp_conf["AMPMGRPASS"])) {
397     return $astman->disconnect();
398   } else {
399     echo "<h3>Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]."</h3>This module requires access to the Asterisk Manager.  Please ensure Asterisk is running and access to the manager is available.</div>";
400     exit;
401   }
402 }
403
404
405 /** Recursively read voicemail.conf (and any included files)
406  * This function is called by getVoicemailConf()
407  */
408 function parse_voicemailconf($filename, &$vmconf, &$section) {
409   if (is_null($vmconf)) {
410     $vmconf = array();
411   }
412   if (is_null($section)) {
413     $section = "general";
414   }
415  
416   if (file_exists($filename)) {
417     $fd = fopen($filename, "r");
418     while ($line = fgets($fd, 1024)) {
419       if (preg_match("/^\s*(\d+)\s*=>\s*(\d*),(.*),(.*),(.*),(.*)\s*([;#].*)?/",$line,$matches)) {
420         // "mailbox=>password,name,email,pager,options"
421         // this is a voicemail line
422         $vmconf[$section][ $matches[1] ] = array("mailbox"=>$matches[1],
423                   "pwd"=>$matches[2],
424                   "name"=>$matches[3],
425                   "email"=>$matches[4],
426                   "pager"=>$matches[5],
427                   "options"=>array(),
428                   );
429                 
430         // parse options
431         //output($matches);
432         foreach (explode("|",$matches[6]) as $opt) {
433           $temp = explode("=",$opt);
434           //output($temp);
435           if (isset($temp[1])) {
436             list($key,$value) = $temp;
437             $vmconf[$section][ $matches[1] ]["options"][$key] = $value;
438           }
439         }
440       } else if (preg_match("/^\s*(\d+)\s*=>\s*dup,(.*)\s*([;#].*)?/",$line,$matches)) {
441         // "mailbox=>dup,name"
442         // duplace name line
443         $vmconf[$section][ $matches[1] ]["dups"][] = $matches[2];
444       } else if (preg_match("/^\s*#include\s+(.*)\s*([;#].*)?/",$line,$matches)) {
445         // include another file
446         
447         if ($matches[1][0] == "/") {
448           // absolute path
449           $filename = $matches[1];
450         } else {
451           // relative path
452           $filename =  dirname($filename)."/".$matches[1];
453         }
454         
455         parse_voicemailconf($filename, $vmconf, $section);
456         
457       } else if (preg_match("/^\s*\[(.+)\]/",$line,$matches)) {
458         // section name
459         $section = strtolower($matches[1]);
460       } else if (preg_match("/^\s*([a-zA-Z0-9-_]+)\s*=\s*(.*?)\s*([;#].*)?$/",$line,$matches)) {
461         // name = value
462         // option line
463         $vmconf[$section][ $matches[1] ] = $matches[2];
464       }
465     }
466     fclose($fd);
467   }
468 }
469
470 /** Write the voicemail.conf file
471  * This is called by saveVoicemail()
472  * It's important to make a copy of $vmconf before passing it. Since this is a recursive function, has to
473  * pass by reference. At the same time, it removes entries as it writes them to the file, so if you don't have
474  * a copy, by the time it's done $vmconf will be empty.
475 */
476 function write_voicemailconf($filename, &$vmconf, &$section, $iteration = 0) {
477   if ($iteration == 0) {
478     $section = null;
479   }
480  
481   $output = array();
482     
483   // if the file does not, copy if from the template.
484   // TODO: is this logical?
485   // TODO: don't use hardcoded path...?
486   if (!file_exists($filename)) {
487     if (!copy( "/etc/asterisk/voicemail.conf.template", $filename )){
488       return;
489     }
490   }
491  
492     $fd = fopen($filename, "r");
493     while ($line = fgets($fd, 1024)) {
494       if (preg_match("/^(\s*)(\d+)(\s*)=>(\s*)(\d*),(.*),(.*),(.*),(.*)(\s*[;#].*)?$/",$line,$matches)) {
495         // "mailbox=>password,name,email,pager,options"
496         // this is a voicemail line
497         //DEBUG echo "\nmailbox";
498         
499         // make sure we have something as a comment
500         if (!isset($matches[10])) {
501           $matches[10] = "";
502         }
503         
504         // $matches[1] [3] and [4] are to preserve indents/whitespace, we add these back in
505         
506         if (isset($vmconf[$section][ $matches[2] ])) { 
507           // we have this one loaded
508           // repopulate from our version
509           $temp = & $vmconf[$section][ $matches[2] ];
510           
511           $options = array();
512           foreach ($temp["options"] as $key=>$value) {
513             $options[] = $key."=".$value;
514           }
515           
516           $output[] = $matches[1].$temp["mailbox"].$matches[3]."=>".$matches[4].$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options).$matches[10];
517           
518           // remove this one from $vmconf
519           unset($vmconf[$section][ $matches[2] ]);
520         } else {
521           // we don't know about this mailbox, so it must be deleted
522           // (and hopefully not JUST added since we did read_voiceamilconf)
523           
524           // do nothing
525         }
526         
527       } else if (preg_match("/^(\s*)(\d+)(\s*)=>(\s*)dup,(.*)(\s*[;#].*)?$/",$line,$matches)) {
528         // "mailbox=>dup,name"
529         // duplace name line
530         // leave it as-is (for now)
531         //DEBUG echo "\ndup mailbox";
532         $output[] = $line;
533       } else if (preg_match("/^(\s*)#include(\s+)(.*)(\s*[;#].*)?$/",$line,$matches)) {
534         // include another file
535         //DEBUG echo "\ninclude ".$matches[3]."<blockquote>";
536         
537         // make sure we have something as a comment
538         if (!isset($matches[4])) {
539           $matches[4] = "";
540         }
541         
542         if ($matches[3][0] == "/") {
543           // absolute path
544           $include_filename = $matches[3];
545         } else {
546           // relative path
547           $include_filename =  dirname($filename)."/".$matches[3];
548         }
549         
550         $output[] = $matches[1]."#include".$matches[2].$matches[3].$matches[4];
551         write_voicemailconf($include_filename, $vmconf, $section, $iteration+1);
552         
553         //DEBUG echo "</blockquote>";
554         
555       } else if (preg_match("/^(\s*)\[(.+)\](\s*[;#].*)?$/",$line,$matches)) {
556         // section name
557         //DEBUG echo "\nsection";
558         
559         // make sure we have something as a comment
560         if (!isset($matches[3])) {
561           $matches[3] = "";
562         }
563         
564         // check if this is the first run (section is null)
565         if ($section !== null) {
566           // we need to add any new entries here, before the section changes
567           //DEBUG echo "<blockquote><i>";
568           //DEBUG var_dump($vmconf[$section]);
569           if (isset($vmconf[$section])){  //need this, or we get an error if we unset the last items in this section - should probably automatically remove the section/context from voicemail.conf
570             foreach ($vmconf[$section] as $key=>$value) {
571               if (is_array($value)) {
572                 // mailbox line
573                 
574                 $temp = & $vmconf[$section][ $key ];
575                 
576                 $options = array();
577                 foreach ($temp["options"] as $key1=>$value) {
578                   $options[] = $key1."=".$value;
579                 }
580                 
581                 $output[] = $temp["mailbox"]." => ".$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options);
582                 
583                 // remove this one from $vmconf
584                 unset($vmconf[$section][ $key ]);
585                 
586               } else {
587                 // option line
588                 
589                 $output[] = $key."=".$vmconf[$section][ $key ];
590                 
591                 // remove this one from $vmconf
592                 unset($vmconf[$section][ $key ]);
593               }
594             }
595           }
596           //DEBUG echo "</i></blockquote>";
597         }
598         
599         $section = strtolower($matches[2]);
600         $output[] = $matches[1]."[".$section."]".$matches[3];
601         $existing_sections[] = $section; //remember that this section exists
602
603       } else if (preg_match("/^(\s*)([a-zA-Z0-9-_]+)(\s*)=(\s*)(.*?)(\s*[;#].*)?$/",$line,$matches)) {
604         // name = value
605         // option line
606         //DEBUG echo "\noption line";
607         
608         
609         // make sure we have something as a comment
610         if (!isset($matches[6])) {
611           $matches[6] = "";
612         }
613         
614         if (isset($vmconf[$section][ $matches[2] ])) {
615           $output[] = $matches[1].$matches[2].$matches[3]."=".$matches[4].$vmconf[$section][ $matches[2] ].$matches[6];
616           
617           // remove this one from $vmconf
618           unset($vmconf[$section][ $matches[2] ]);
619         }
620         // else it's been deleted, so we don't write it in
621         
622       } else {
623         // unknown other line -- probably a comment or whitespace
624         //DEBUG echo "\nother: ".$line;
625         
626         $output[] = str_replace(array("\n","\r"),"",$line); // str_replace so we don't double-space
627       }
628     }
629     
630     if (($iteration == 0) && (is_array($vmconf))) {
631       // we need to add any new entries here, since it's the end of the file
632       //DEBUG echo "END OF FILE!! <blockquote><i>";
633       //DEBUG var_dump($vmconf);
634       foreach (array_keys($vmconf) as $section) {
635         if (!in_array($section,$existing_sections))  // If this is a new section, write the context label
636           $output[] = "[".$section."]";
637         foreach ($vmconf[$section] as $key=>$value) {
638           if (is_array($value)) {
639             // mailbox line
640             
641             $temp = & $vmconf[$section][ $key ];
642             
643             $options = array();
644             foreach ($temp["options"] as $key=>$value) {
645               $options[] = $key."=".$value;
646             }
647             
648             $output[] = $temp["mailbox"]." => ".$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options);
649             
650             // remove this one from $vmconf
651             unset($vmconf[$section][ $key ]);
652             
653           } else {
654             // option line
655             
656             $output[] = $key."=".$vmconf[$section][ $key ];
657             
658             // remove this one from $vmconf
659             unset($vmconf[$section][$key ]);
660           }
661         }
662       }
663       //DEBUG echo "</i></blockquote>";
664     }
665     
666     fclose($fd);
667     
668     //DEBUG echo "\n\nwriting ".$filename;
669     //DEBUG echo "\n-----------\n";
670     //DEBUG echo implode("\n",$output);
671     //DEBUG echo "\n-----------\n";
672     
673     // write this file back out
674     
675     if ($fd = fopen($filename, "w")) {
676       fwrite($fd, implode("\n",$output)."\n");
677       fclose($fd);
678     }
679     
680 }
681
682
683 // $goto is the current goto destination setting
684 // $i is the destination set number (used when drawing multiple destination sets in a single form ie: digital receptionist)
685 // esnure that any form that includes this calls the setDestinations() javascript function on submit.
686 // ie: if the form name is "edit", and drawselects has been called with $i=2 then use onsubmit="setDestinations(edit,2)"
687 function drawselects($goto,$i) { 
688  
689   /* --- MODULES BEGIN --- */
690   global $active_modules;
691  
692   // This is purely to remove a warning.
693   if (!isset($selectHtml)) { $selectHtml=''; }
694   $selectHtml .= '<tr><td colspan=2>';
695  
696   //check for module-specific destination functions
697   foreach ($active_modules as $mod => $displayname) {
698     $funct = strtolower($mod.'_destinations');
699  
700     //if the modulename_destinations() function exits, run it and display selections for it
701     if (function_exists($funct)) {
702       $options = "";
703       $destArray = $funct(); //returns an array with 'destination' and 'description'.
704       $checked = false;
705       if (isset($destArray)) {
706         //loop through each option returned by the module
707         foreach ($destArray as $dest) {
708           // check to see if the currently selected goto matches one these destinations
709           if ($dest['destination'] == $goto)
710             $checked = true;  //there is a match, so we select the radio for this group
711
712           // create an select option for each destination
713           $options .= '<option value="'.$dest['destination'].'" '.(strpos($goto,$dest['destination']) === false ? '' : 'SELECTED').'>'.($dest['description'] ? $dest['description'] : $dest['destination']);
714         }
715         
716         // make a unique id to be used for the HTML id
717         // This allows us to have multiple drawselect() sets on the page without
718         // conflicting with each other
719         $radioid = uniqid("drawselect");
720         
721         $selectHtml .=  '<input type="radio" id="'.$radioid.'" name="goto_indicate'.$i.'" value="'.$mod.'" '.
722                 'onclick="javascript:this.form.goto'.$i.'.value=\''.$mod.'\';" '.
723                 'onkeypress="javascript:if (event.keyCode == 0 || (document.all && event.keyCode == 13)) this.form.goto'.$i.'.value=\''.$mod.'\';" '.
724                 ($checked? 'CHECKED=CHECKED' : '').' /> ';
725         $selectHtml .= '<label for="'.$radioid.'">'._($displayname['displayname']).':</label> ';
726         if ($checked) { $goto = $mod; }
727         $selectHtml .=  '<select name="'.$mod.$i.'" onfocus="document.getElementById(\''.$radioid.'\').checked = true; this.form.goto'.$i.'.value=\''.$mod.'\';">';
728         $selectHtml .= $options; 
729         $selectHtml .=  "</select><br>\n";
730       }
731       
732     }
733   }
734   /* --- MODULES END --- */
735  
736   //display a custom goto field
737   $radioid = uniqid("drawselect");
738   $selectHtml .= '<input type="radio" id="'.$radioid.'" name="goto_indicate'.$i.'" value="custom" onclick="javascript:this.form.goto'.$i.'.value=\'custom\';" onkeypress="javascript:if (event.keyCode == 0 || (document.all && event.keyCode == 13)) this.form.goto'.$i.'.value=\'custom\';" '.(strpos($goto,'custom') === false ? '' : 'CHECKED=CHECKED').' />';
739   $selectHtml .= '<a href="#" class="info"> '._("Custom App<span><br>ADVANCED USERS ONLY<br><br>Uses Goto() to send caller to a custom context.<br><br>The context name <b>MUST</b> contain the word 'custom' and should be in the format custom-context , extension , priority. Example entry:<br><br><b>custom-myapp,s,1</b><br><br>The <b>[custom-myapp]</b> context would need to be created and included in extensions_custom.conf</span>").'</a>:';
740   $selectHtml .= '<input type="text" size="15" name="custom'.$i.'" value="'.(strpos($goto,'custom') === false ? '' : $goto).'" onfocus="document.getElementById(\''.$radioid.'\').checked = true;" />';
741   $selectHtml .= "\n<input type='hidden' name='goto$i' value='$goto'>";
742
743   //close off our row
744   $selectHtml .= '</td></tr>';
745  
746   return $selectHtml;
747 }
748
749
750 /* below are legacy functions required to allow pre 2.0 modules to function (ie: interact with 'extensions' table) */
751
752   //add to extensions table - used in callgroups.php
753   function legacy_extensions_add($addarray) {
754     global $db;
755     $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr, flags) VALUES ('".$addarray[0]."', '".$addarray[1]."', '".$addarray[2]."', '".$addarray[3]."', '".$addarray[4]."', '".$addarray[5]."' , '".$addarray[6]."')";
756     $result = $db->query($sql);
757     if(DB::IsError($result)) {
758       die($result->getMessage().$sql);
759     }
760     return $result;
761   }
762  
763   //delete extension from extensions table
764   function legacy_extensions_del($context,$exten) {
765     global $db;
766     $sql = "DELETE FROM extensions WHERE context = '".addslashes($context)."' AND `extension` = '".addslashes($exten)."'";
767     $result = $db->query($sql);
768     if(DB::IsError($result)) {
769       die($result->getMessage());
770     }
771     return $result;
772   }
773  
774  
775   //get args for specified exten and priority - primarily used to grab goto destination
776   function legacy_args_get($exten,$priority,$context) {
777     global $db;
778     $sql = "SELECT args FROM extensions WHERE extension = '".addslashes($exten)."' AND priority = '".addslashes($priority)."' AND context = '".addslashes($context)."'";
779     list($args) = $db->getRow($sql);
780     return $args;
781   }
782
783 /* end legacy functions */
784
785 /* Usage
786 Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML;
787
788 $objXML = new xml2Array();
789 $arrOutput = $objXML->parse($strYourXML);
790 print_r($arrOutput); //print it out, or do whatever!
791
792 */
793
794 class xml2Array {
795  
796   var $arrOutput = array();
797   var $resParser;
798   var $strXmlData;
799  
800   function parse($strInputXML) {
801  
802       $this->resParser = xml_parser_create ();
803       xml_set_object($this->resParser,$this);
804       xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
805       
806       xml_set_character_data_handler($this->resParser, "tagData");
807     
808       $this->strXmlData = xml_parse($this->resParser,$strInputXML );
809       if(!$this->strXmlData) {
810         die(sprintf("XML error: %s at line %d",
811       xml_error_string(xml_get_error_code($this->resParser)),
812       xml_get_current_line_number($this->resParser)));
813       }
814               
815       xml_parser_free($this->resParser);
816       
817       return $this->arrOutput;
818   }
819   function tagOpen($parser, $name, $attrs) {
820     $tag=array("name"=>$name,"attrs"=>$attrs);
821     array_push($this->arrOutput,$tag);
822   }
823  
824   function tagData($parser, $tagData) {
825     if(trim($tagData)) {
826       if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) {
827         $this->arrOutput[count($this->arrOutput)-1]['tagData'] .= "\n".$tagData;
828       }
829       else {
830         $this->arrOutput[count($this->arrOutput)-1]['tagData'] = $tagData;
831       }
832     }
833   }
834  
835   function tagClosed($parser, $name) {
836     $this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this->arrOutput)-1];
837     array_pop($this->arrOutput);
838   }
839  
840   function recursive_parseLevel($items) {
841     $array = array();
842     foreach (array_keys($items) as $idx) {
843       $items[$idx]['name'] = strtolower($items[$idx]['name']);
844       
845       $multi = false;
846       if (isset($array[ $items[$idx]['name'] ])) {
847         // this child is already set, so we're adding multiple items to an array
848         
849         if (!is_array($array[ $items[$idx]['name'] ]) || !isset($array[ $items[$idx]['name'] ][0])) {
850           // hasn't already been made into a numerically-indexed array, so do that now
851           // we're basically moving the current contents of this item into a 1-item array (at the
852           // original location) so that we can add a second item in the code below
853           $array[ $items[$idx]['name'] ] = array( $array[ $items[$idx]['name'] ] );
854         }
855         $multi = true;
856       }
857       
858       if (isset($items[$idx]['children']) && is_array($items[$idx]['children'])) {
859         if ($multi) {
860           $array[ $items[$idx]['name'] ][] = $this->recursive_parseLevel($items[$idx]['children']);
861         } else {
862           $array[ $items[$idx]['name'] ] = $this->recursive_parseLevel($items[$idx]['children']);
863         }
864       } else if (isset($items[$idx]['tagData'])) {
865         if ($multi) {
866           $array[ $items[$idx]['name'] ][] = $items[$idx]['tagData'];
867         } else {
868           $array[ $items[$idx]['name'] ] = $items[$idx]['tagData'];
869         }
870       }
871     }
872     return $array;
873   }
874  
875   function parseAdvanced($strInputXML) {
876     $array = $this->parse($strInputXML);
877     return $this->recursive_parseLevel($array);
878   }
879 }
880
881 /*
882   Return a much more manageable assoc array with module data.
883 */
884 class xml2ModuleArray extends xml2Array {
885   function parseModulesXML($strInputXML) {
886     $array = $this->parseAdvanced($strInputXML);
887     if (isset($array['xml'])) {
888       foreach ($array['xml'] as $key=>$module) {
889         if ($key == 'module') {
890           // copy the structure verbatim
891           $modules[ $module['name'] ] = $module;
892           // add in a couple that aren't normally there..
893           $modules[ $module['name'] ] = $module;
894         }
895       }
896     }
897     
898     // if you are confused about what's happening below, uncomment this why we do it
899     // echo "<pre>"; print_r($arrOutput); echo "</pre>";
900     
901     // ignore the regular xml garbage ([0]['children']) & loop through each module
902     if(!is_array($arrOutput[0]['children'])) return false;
903     foreach($arrOutput[0]['children'] as $module) {
904       if(!is_array($module['children'])) return false;
905       // loop through each modules's tags
906       foreach($module['children'] as $modTags) {
907           if(isset($modTags['children']) && is_array($modTags['children'])) {
908             $$modTags['name'] = $modTags['children'];
909             // loop if there are children (menuitems and requirements)
910             foreach($modTags['children'] as $subTag) {
911               $subTags[strtolower($subTag['name'])] = $subTag['tagData'];
912             }
913             $$modTags['name'] = $subTags;
914             unset($subTags);
915           } else {
916             // create a variable for each tag we find
917             $$modTags['name'] = $modTags['tagData'];
918           }
919
920       }
921       // now build our return array
922       $arrModules[$RAWNAME]['rawname'] = $RAWNAME;    // This has to be set
923       $arrModules[$RAWNAME]['displayName'] = $NAME;    // This has to be set
924       $arrModules[$RAWNAME]['version'] = $VERSION;     // This has to be set
925       $arrModules[$RAWNAME]['type'] = isset($TYPE)?$TYPE:'setup';
926       $arrModules[$RAWNAME]['category'] = isset($CATEGORY)?$CATEGORY:'Unknown';
927       $arrModules[$RAWNAME]['info'] = isset($INFO)?$INFO:'http://www.freepbx.org/wiki/'.$RAWNAME;
928       $arrModules[$RAWNAME]['location'] = isset($LOCATION)?$LOCATION:'local';
929       $arrModules[$RAWNAME]['items'] = isset($MENUITEMS)?$MENUITEMS:null;
930       $arrModules[$RAWNAME]['requirements'] = isset($REQUIREMENTS)?$REQUIREMENTS:null;
931       $arrModules[$RAWNAME]['md5sum'] = isset($MD5SUM)?$MD5SUM:null;
932       //print_r($arrModules);
933       //unset our variables
934       unset($NAME);
935       unset($VERSION);
936       unset($TYPE);
937       unset($CATEGORY);
938       unset($AUTHOR);
939       unset($EMAIL);
940       unset($LOCATION);
941       unset($MENUITEMS);
942       unset($REQUIREMENTS);
943       unset($MD5SUM);
944     }
945     //echo "<pre>"; print_r($arrModules); echo "</pre>";
946
947     return $arrModules;
948   }
949 }
950
951 // get_headers() for php4 (built in for php5)
952 if (!function_existS('get_headers')) {
953   function get_headers($url ) {
954     $url_info=parse_url($url);
955     if (isset($url_info['scheme']) && $url_info['scheme'] == 'https') {
956       $port = isset($url_info['port']) ? $url_info['port'] : 443;
957       @$fp=fsockopen('ssl://'.$url_info['host'], $port, $errno, $errstr, 10);
958     } else {
959       $port = isset($url_info['port']) ? $url_info['port'] : 80;
960       @$fp=fsockopen($url_info['host'], $port, $errno, $errstr, 10);
961     }
962     if ($fp) {
963       stream_set_timeout($fp, 10);
964       $head = "HEAD ".@$url_info['path']."?".@$url_info['query'];
965       $head .= " HTTP/1.0\r\nHost: ".@$url_info['host']."\r\n\r\n";
966       fputs($fp, $head);
967       while(!feof($fp)) {
968         if($header=trim(fgets($fp, 1024))) {
969           $sc_pos = strpos($header, ':');
970           if ($sc_pos === false) {
971             $headers['status'] = $header;
972           } else {
973             $label = substr( $header, 0, $sc_pos );
974             $value = substr( $header, $sc_pos+1 );
975             $headers[strtolower($label)] = trim($value);
976           }
977         }
978       }
979       return $headers;
980     } else {
981       return false;
982     }
983   }
984 }
985   
986
987 class moduleHook {
988   var $hookHtml = '';
989   var $arrHooks = array();
990  
991   function install_hooks($viewing_itemid,$target_module,$target_menuid = '') {
992     global $active_modules;
993     // loop through all active modules
994     foreach($active_modules as $this_module) {
995         // look for requested hooks for $module
996         // ie: findme_hook_extensions()
997         $funct = $this_module['rawname'] . '_hook_' . $target_module;
998         if( function_exists( $funct ) ) {
999           // execute the function, appending the
1000           // html output to that of other hooking modules
1001           if ($hookReturn = $funct($viewing_itemid,$target_menuid))
1002             $this->hookHtml .= $hookReturn;
1003           // remember who installed hooks
1004           // we need to know this for processing form vars
1005           $this->arrHooks[] = $this_module['rawname'];
1006         }
1007     }
1008   }
1009  
1010   // process the request from the module we hooked
1011   function process_hooks($viewing_itemid, $target_module, $target_menuid, $request) {
1012     if(is_array($this->arrHooks)) {
1013       foreach($this->arrHooks as $hookingMod) {
1014         // check if there is a processing function
1015         $funct = $hookingMod . '_hookProcess_' . $target_module;
1016         if( function_exists( $funct ) ) {
1017           $funct($viewing_itemid, $request);
1018         }
1019       }
1020     }
1021   }
1022 }
1023
1024 function execSQL( $file )
1025 {
1026   global $db;
1027   $data = null;
1028  
1029   // run sql script
1030   $fd = fopen( $file ,"r" );
1031  
1032   while (!feof($fd)) {
1033     $data .= fread($fd, 1024);
1034   }
1035   fclose($fd);
1036  
1037   preg_match_all("/((SELECT|INSERT|UPDATE|DELETE|CREATE|DROP).*);\s*\n/Us", $data, $matches);
1038   foreach ($matches[1] as $sql) {
1039     $result = $db->query($sql);
1040     if(DB::IsError($result)) { return false; }
1041   }
1042 }
1043
1044 // Dragged this in from page.modules.php, so it can be used by install_amp.
1045 function runModuleSQL($moddir,$type){
1046   trigger_error("runModuleSQL() is depreciated - please use _module_runscripts(), or preferably module_install() or module_enable() instead", E_USER_WARNING);
1047   _module_runscripts($moddir, $type);
1048 }
1049
1050
1051
1052 /*
1053 // just for testing hooks, i'll delete it later
1054 function queues_hook_core($viewing_itemid, $target_menuid) {
1055   switch ($target_menuid) {
1056     case 'did':
1057       //get the current setting for this display (if any)
1058       $alertinfo = $viewing_itemid;
1059           return '
1060         <tr>
1061           <td><a href="#" class="info">'._("Alert Info").'<span>'._('ALERT_INFO can be used for distinctive ring with SIP devices.').'</span></a>:</td>
1062           <td><input type="text" name="alertinfo" size="10" value="'.(($alertinfo) ? $alertinfo : "") .'"></td>
1063         </tr>
1064       ';
1065     break;
1066     default:
1067       return false;
1068     break;
1069   }
1070 }
1071
1072 function queues_hookProcess_core($viewing_itemid, $request) {
1073   switch ($request['action']) {
1074     case 'edtIncoming':
1075       echo "<h1>HI</h1>";
1076           return '
1077         <tr>
1078           <td><a href="#" class="info">'._("Alert Info").'<span>'._('ALERT_INFO can be used for distinctive ring with SIP devices.').'</span></a>:</td>
1079           <td><input type="text" name="alertinfo" size="10" value="'.(($alertinfo) ? $alertinfo : "") .'"></td>
1080         </tr>
1081       ';
1082     break;
1083     default:
1084       return false;
1085     break;
1086   }
1087 }
1088 */
1089
1090 /** Replaces variables in a string with the values from ampconf
1091  * eg, "%AMPWEBROOT%/admin" => "/var/www/html/admin"
1092  */
1093 function ampconf_string_replace($string) {
1094   global $amp_conf;
1095  
1096   $target = array();
1097   $replace = array();
1098  
1099   foreach ($amp_conf as $key=>$value) {
1100     $target[] = '%'.$key.'%';
1101     $replace[] = $value;
1102   }
1103  
1104   return str_replace($target, $replace, $string);
1105 }
1106
1107 /***********************************************************************************************************
1108                                        Module functions
1109 ************************************************************************************************************/
1110  
1111 /** Get the latest module.xml file for this freePBX version.
1112  * Caches in the database for 5 mintues.
1113  * If $module is specified, only returns the data for that module
1114  */
1115 function module_getonlinexml($module = false) { // was getModuleXml()
1116   global $amp_conf;
1117   //this should be in an upgrade file ... putting here for now.
1118   sql('CREATE TABLE IF NOT EXISTS module_xml (time INT NOT NULL , data BLOB NOT NULL) TYPE = MYISAM ;');
1119  
1120   $result = sql('SELECT * FROM module_xml','getRow',DB_FETCHMODE_ASSOC);
1121   // if the epoch in the db is more than 2 hours old, or the xml is less than 100 bytes, then regrab xml
1122   // Changed to 5 minutes while not in release. Change back for released version.
1123   //
1124   // used for debug, time set to 0 to always fall through
1125   // if((time() - $result['time']) > 0 || strlen($result['data']) < 100 ) {
1126   if((time() - $result['time']) > 300 || strlen($result['data']) < 100 ) {
1127     $version = getversion();
1128     $version = $version[0][0];
1129     // we need to know the freepbx major version we have running (ie: 2.1.2 is 2.1)
1130     preg_match('/(\d+\.\d+)/',$version,$matches);
1131     //echo "the result is ".$matches[1];
1132     if (isset($amp_conf["AMPMODULEXML"])) {
1133       $fn = $amp_conf["AMPMODULEXML"]."modules-".$matches[1].".xml";
1134       // echo "(From amportal.conf)"; //debug
1135     } else {
1136     $fn = "http://mirror.freepbx.org/modules-".$matches[1].".xml";
1137       // echo "(From default)"; //debug
1138     }
1139     //$fn = "/usr/src/freepbx-modules/modules.xml";
1140     $data = file_get_contents($fn);
1141     // remove the old xml
1142     sql('DELETE FROM module_xml');
1143     // update the db with the new xml
1144     $data4sql = addslashes($data);
1145     sql('INSERT INTO module_xml (time,data) VALUES ('.time().',"'.$data4sql.'")');
1146   } else {
1147 //    echo "using cache";
1148     $data = $result['data'];
1149   }
1150   //echo time() - $result['time'];
1151   $parser = new xml2ModuleArray($data);
1152   $xmlarray = $parser->parseAdvanced($data);
1153   //$modules = $xmlarray['XML']['MODULE'];
1154  
1155   //echo "<hr>Raw XML Data<pre>"; print_r(htmlentities($data)); echo "</pre>";
1156   //echo "<hr>XML2ARRAY<pre>"; print_r($xmlarray); echo "</pre>";
1157  
1158  
1159   if (isset($xmlarray['xml']['module'])) {
1160  
1161     if ($module != false) {
1162       foreach ($xmlarray['xml']['module'] as $mod) {
1163         if ($module == $mod['rawname']) {
1164           return $mod;
1165         }
1166       }
1167       return null;
1168     } else {
1169     
1170     
1171       $modules = array();
1172       foreach ($xmlarray['xml']['module'] as $mod) {
1173         $modules[ $mod['rawname'] ] = $mod;
1174       }
1175       return $modules;
1176     }
1177   }
1178   return null;
1179 }
1180
1181 /** Looks through the modules directory and modules database and returns all available
1182  * information about one or all modules
1183  * @param string  (optional) The module name to query, or false for all module
1184  * @param mixed   (optional) The status(es) to show, using MODULE_STATUS_* constants. Can
1185  *                either be one value, or an array of values.
1186  */
1187 function module_getinfo($module = false, $status = false) {
1188   global $amp_conf, $db;
1189   $modules = array();
1190  
1191   if ($module) {
1192     // get info on only one module
1193     $xml = _module_readxml($module);
1194     if (!is_null($xml)) {
1195       $modules[$module] = $xml;
1196       // if status is anything else, it will be updated below when we read the db
1197       $modules[$module]['status'] = MODULE_STATUS_NOTINSTALLED;
1198     }
1199     
1200     // query to get just this one
1201     $sql = 'SELECT * FROM modules WHERE modulename = "'.$module.'"';
1202   } else {
1203     // get info on all modules
1204     $dir = opendir($amp_conf['AMPWEBROOT'].'/admin/modules');
1205     while ($file = readdir($dir)) {
1206       if (($file != ".") && ($file != "..") && ($file != "CVS") &&
1207           ($file != ".svn") && ($file != "_cache") &&
1208         is_dir($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file)) {
1209         
1210         $xml = _module_readxml($file);
1211         if (!is_null($xml)) {
1212           $modules[$file] = $xml;
1213           // if status is anything else, it will be updated below when we read the db
1214           $modules[$file]['status'] = MODULE_STATUS_NOTINSTALLED;
1215         }
1216       }
1217     }
1218     
1219     // query to get everything
1220     $sql = 'SELECT * FROM modules';
1221   }
1222   // determine details about this module from database
1223   // modulename should match the directory name
1224  
1225   $results = $db->getAll($sql,DB_FETCHMODE_ASSOC);
1226   if(DB::IsError($results)) {
1227     die($results->getMessage());
1228   }
1229  
1230   if (is_array($results)) {
1231     foreach($results as $row) {
1232       if (isset($modules[ $row['modulename'] ])) {
1233         if ($row['enabled'] != 0) {
1234           
1235           // check if file and registered versions are the same
1236           // version_compare returns 0 if no difference
1237           if (version_compare($row['version'], $modules[ $row['modulename'] ]['version']) == 0) {
1238             $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_ENABLED;
1239           } else {
1240             $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_NEEDUPGRADE;
1241           }
1242           
1243         } else {
1244           $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_DISABLED;
1245         }
1246       } else {
1247         // no directory for this db entry
1248         $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_BROKEN;
1249       }
1250       $modules[ $row['modulename'] ]['dbversion'] = $row['version'];
1251     }
1252   }
1253  
1254   if ($status !== false) {
1255     if (!is_array($status)) {
1256       // make a one element array so we can use in_array below
1257       $status = array($status);
1258     }
1259     
1260     foreach (array_keys($modules) as $name) {
1261       if (!in_array($modules[$name]['status'], $status)) {
1262         // not found in the $status array, remove it
1263         unset($modules[$name]);
1264       }
1265     }
1266   }
1267  
1268   return $modules;
1269 }
1270
1271 /** Check if a module meets dependencies.
1272  * @param  mixed  The name of the module, or the modulexml Array
1273  * @return mixed  Returns true if dependencies are met, or an array
1274  *                containing a list of human-readable errors if not.
1275  *                NOTE: you must use strict type checking (===) to test
1276  *                for true, because  array() == true !
1277  */
1278 function module_checkdepends($modulename) {
1279  
1280   // check if we were passed a modulexml array, or a string (name)
1281   // ensure $modulexml is the modules array, and $modulename is the name (as a string)
1282   if (is_array($modulename)) {
1283     $modulexml = $modulename;
1284     $modulename = $modulename['rawname'];
1285   } else {
1286     $modulexml = module_getinfo($modulename);
1287   }
1288  
1289   $errors = array();
1290  
1291   // special handling for engine
1292   $engine_dependency = false; // if we've found ANY engine dependencies to check
1293   $engine_matched = false; // if an engine dependency has matched
1294   $engine_errors = array(); // the error strings for engines
1295  
1296   if (isset($modulexml['depends'])) {
1297     foreach ($modulexml['depends'] as $type => $requirements) {
1298       // if only a single item, make it an array so we can use the same code as for multiple items
1299       // this is because if there is  <module>a</module><module>b</module>  we will get array('module' => array('a','b'))
1300       if (!is_array($requirements)) {
1301         $requirements = array($requirements);
1302       }
1303       
1304       foreach ($requirements as $value) {
1305         switch ($type) {
1306           case 'version':
1307             if (preg_match('/^(lt|le|gt|ge|==|=|eq|!=|ne)?\s*(\d(\.\d)*)$/i', $value, $matches)) {
1308               // matches[1] = operator, [2] = version
1309               $ver = getversion();
1310               $ver = $ver[0][0]; // dumb PEARDB thing
1311               $operator = (!empty($matches[1]) ? $matches[1] : 'ge'); // default to >=
1312               if (version_compare($matches[2], $ver, $operator) ) {
1313                 $errors[] = _module_comparison_error_message('FreePBX', $matches[2], $ver, $operator);
1314               }
1315             }
1316           break;
1317           case 'module':
1318             if (preg_match('/^([a-z0-9_]+)(\s+(lt|le|gt|ge|==|=|eq|!=|ne)?\s*(\d(\.\d)*))?$/i', $value, $matches)) {
1319               // matches[1] = modulename, [3]=comparison operator, [4] = version
1320               $modules = module_getinfo($matches[1]);
1321               if (isset($modules[$matches[1]])) {
1322                 switch ($modules[$matches[1]]['status'] ) {
1323                   case MODULE_STATUS_ENABLED:
1324                     if (!empty($matches[4])) {
1325                       // also doing version checking
1326                       $operator = (!empty($matches[3]) ? $matches[3] : 'ge'); // default to >=
1327                       if (version_compare($matches[4], $modules[$matches[1]]['dbversion'], $operator) ) {
1328                         $errors[] = _module_comparison_error_message($matches[1].' module', $matches[4], $modules[$matches[1]]['dbversion'], $operator);
1329                       }
1330                     }
1331                   break;
1332                   case MODULE_STATUS_BROKEN:
1333                     $errors[] = sprintf(_('Module %s is required, but yours is broken. You should reinstall '.
1334                                           'it and try again.'), $matches[1]);
1335                   break;
1336                   case MODULE_STATUS_DISABLED:
1337                     $errors[] = sprintf(_('Module %s is required, but yours is disabled.'), $matches[1]);
1338                   break;
1339                   case MODULE_STATUS_NEEDUPGRADE:
1340                     $errors[] = sprintf(_('Module %s is required, but yours is disabled because it needs to '.
1341                                           'be upgraded. Please upgrade %s first, and then try again.'),
1342                               $matches[1], $matches[1]);
1343                   break;
1344                   default:
1345                   case MODULE_STATUS_NOTINSTALLED:
1346                     $errors[] = sprintf(_('Module %s is required, yours is not installed.'), $matches[1]);
1347                   break;
1348                 }
1349               } else {
1350                 $errors[] = sprintf(_('Module %s is required.'), $matches[1]);
1351               }
1352             }
1353           break;
1354           case 'file': // file exists
1355             // replace embedded amp_conf %VARIABLES% in string
1356             $file = ampconf_string_replace($value);
1357             
1358             if (!file_exists( $file )) {
1359               $errors[] = sprintf(_('File %s must exist.'), $file);
1360             }
1361           break;
1362           case 'engine':
1363             /****************************
1364              *  NOTE: there is special handling for this check. We want to "OR" conditions, instead of
1365              *        "AND"ing like the rest of them.
1366              */
1367             
1368             // we found at least one engine, so mark that we're matching this
1369             $engine_dependency = true;
1370             
1371             if (preg_match('/^([a-z0-9_]+)(\s+(lt|le|gt|ge|==|=|eq|!=|ne)?\s*(\d(\.\d)*))?$/i', $value, $matches)) {
1372               // matches[1] = engine, [3]=comparison operator, [4] = version
1373               $operator = (!empty($matches[3]) ? $matches[3] : 'ge'); // default to >=
1374               
1375               $engine = engine_getinfo();
1376               if (($engine['engine'] == $matches[1]) &&
1377                   (empty($matches[4]) || !version_compare($matches[4], $engine['version'], $operator))
1378                  ) {
1379                 
1380                 $engine_matched = true;
1381               } else {
1382                 // add it to the error messages
1383                 if ($matches[4]) {
1384                   // version specified
1385                   $operator_friendly = str_replace(array('gt','ge','lt','le','eq','ne'), array('>','>=','<','<=','=','not ='), $operator);
1386                   $engine_errors[] = $matches[1].' ('.$operator_friendly.' '.$matches[4].')';
1387                 } else {
1388                   // no version
1389                   $engine_errors[] = $matches[1];
1390                 }
1391               }
1392             }
1393           break;
1394         }
1395       }
1396     }
1397     
1398     // special handling for engine
1399     // if we've had at least one engine dependency check, and no engine dependencies matched, we have an error
1400     if ($engine_dependency && !$engine_matched) {
1401     
1402       $engineinfo = engine_getinfo();
1403       $yourengine = $engineinfo['engine'].' '.$engineinfo['version'];
1404       // print it nicely
1405       if (count($engine_errors) == 1) {
1406         $errors[] = sprintf(_('Requires engine %s, you have: %s'),$engine_errors[0],$yourengine);
1407       } else {
1408         $errors[] = sprintf(_('Requires one of the following engines: %s; you have: %s'),implode(', ', $engine_errors),$yourengine);
1409       }
1410     }
1411   }
1412  
1413   if (count($errors) > 0) {
1414     return $errors;
1415   } else {
1416     return true;
1417   }
1418 }
1419
1420 function _module_comparison_error_message($module, $reqversion, $version, $operator) {
1421   switch ($operator) {
1422     case 'lt': case '<':
1423       return sprintf(_('A %s version below %s is required, you have %s'), $module, $reqversion, $version);
1424     break;
1425     case 'le': case '<=';
1426       return sprintf(_('%s version %s or below is required, you have %s'), $module, $reqversion, $version);
1427     break;
1428     case 'gt': case '>';
1429       return sprintf(_('A %s version newer than %s required, you have %s'), $module, $reqversion, $version);
1430     break;
1431     case 'ne': case '!=': case '<>':
1432       return sprintf(_('Your %s version (%s) is incompatible.'), $version, $reqversion);
1433     break;
1434     case 'eq': case '==': case '=':
1435       return sprintf(_('Only %s version %s is compatible, you have %s'), $module, $reqversion, $version);
1436     break;
1437     default:
1438     case 'ge': case '>=':
1439       return sprintf(_('%s version %s or higher is required, you have %s'), $module, $reqversion, $version);
1440   }
1441 }
1442
1443 /** Finds all the enabled modules that depend on a given module
1444  * @param  mixed  The name of the module, or the modulexml Array
1445  * @return array  Array containing the list of modules, or false if no dependencies
1446  */
1447 function module_reversedepends($modulename) {
1448   // check if we were passed a modulexml array, or a string (name)
1449   // ensure $modulename is the name (as a string)
1450   if (is_array($modulename)) {
1451     $modulename = $modulename['rawname'];
1452   }
1453  
1454   $modules = module_getinfo(false, MODULE_STATUS_ENABLED);
1455  
1456   $depends = array();
1457  
1458   foreach (array_keys($modules) as $name) {
1459     if (isset($modules[$name]['depends'])) {
1460       foreach ($modules[$name]['depends'] as $type => $requirements) {
1461         if ($type == 'module') {
1462           // if only a single item, make it an array so we can use the same code as for multiple items
1463           // this is because if there is  <module>a</module><module>b</module>  we will get array('module' => array('a','b'))
1464           if (!is_array($requirements)) {
1465             $requirements = array($requirements);
1466           }
1467           
1468           foreach ($requirements as $value) {
1469             if (preg_match('/^([a-z0-9_]+)(\s+(>=|>|=|<|<=|!=)?\s*(\d(\.\d)*))?$/i', $value, $matches)) {
1470               // matches[1] = modulename, [3]=comparison operator, [4] = version
1471               
1472               // note, we're not checking version here. Normally this function is used when
1473               // uninstalling a module, so it doesn't really matter anyways, and version
1474               // dependency should have already been checked when the module was installed
1475               if ($matches[1] == $modulename) {
1476                 $depends[] = $name;
1477               }
1478             }
1479           }
1480         }
1481       }
1482     }
1483   }
1484  
1485   return (count($depends) > 0) ? $depends : false;
1486 }
1487
1488
1489 /** Enables a module
1490  * @param string    The name of the module to enable
1491  * @param bool      If true, skips status and dependency checks
1492  * @return  mixed   True if succesful, array of error messages if not succesful
1493  */
1494 function module_enable($modulename, $force = false) { // was enableModule
1495   $modules = module_getinfo($modulename);
1496  
1497   if ($modules[$modulename]['status'] == MODULE_STATUS_ENABLED) {
1498     return array(_("Module is already enabled"));
1499   }
1500  
1501   // doesn't make sense to skip this on $force - eg, we can't enable a non-installed or broken module
1502   if ($modules[$modulename]['status'] != MODULE_STATUS_DISABLED) {
1503     return array(_("Module cannot be enabled"));
1504   }
1505  
1506   if (!$force) {
1507     if (($errors = module_checkdepends($modules[$modulename])) !== true) {
1508       return $errors;
1509     }
1510   }
1511  
1512   // disabled (but doesn't needupgrade or need install), and meets dependencies
1513   _module_setenabled($modulename, true);
1514   return true;
1515 }
1516
1517 /** Downloads the latest version of a module
1518  * and extracts it to the directory
1519  * @param string    The name of the module to install
1520  * @param bool      If true, skips status and dependency checks
1521  * @param string    The name of a callback function to call with progress updates.
1522                     function($action, $params). Possible actions:
1523                       getinfo: while downloading modules.xml
1524                       downloading: while downloading file; params include 'read' and 'total'
1525                       untar: before untarring
1526                       done: when complete
1527  * @return  mixed   True if succesful, array of error messages if not succesful
1528  */
1529 function module_download($modulename, $force = false, $progress_callback = null) { // was fetchModule
1530   global $amp_conf;
1531  
1532   // size of download blocks to fread()
1533   // basically, this controls how often progress_callback is called
1534   $download_chunk_size = 12*1024;
1535  
1536   // invoke progress callback
1537   if (function_exists($progress_callback)) {
1538     $progress_callback('getinfo', array('module'=>$modulename));
1539   }
1540       
1541   $res = module_getonlinexml($modulename);
1542   if ($res == null) {
1543     return array(_("Module not found in repository"));
1544   }
1545  
1546   $file = basename($res['location']);
1547   $filename = $amp_conf['AMPWEBROOT']."/admin/modules/_cache/".$file;
1548   // if we're not forcing the download, and a file with the target name exists..
1549   if (!$force && file_exists($filename)) {
1550     // We might already have it! Let's check the MD5.
1551     $filedata = "";
1552     if ( $fh = @ fopen($filename, "r") ) {
1553       while (!feof($fh)) {
1554         $filedata .= fread($fh, 8192);
1555       }
1556       fclose($fh);
1557     }
1558     
1559     if (isset($res['md5sum']) && $res['md5sum'] == md5 ($filedata)) {
1560       // Note, if there's no MD5 information, it will redownload
1561       // every time. Otherwise theres no way to avoid a corrupt
1562       // download
1563       
1564       // invoke progress callback
1565       if (function_exists($progress_callback)) {
1566         $progress_callback('untar', array('module'=>$modulename, 'size'=>filesize($filename)));
1567       }
1568       
1569       exec("tar zxf ".escapeshellarg($filename)." --directory=".escapeshellarg($amp_conf['AMPWEBROOT'].'/admin/modules/'), $output, $exitcode);
1570       if ($exitcode != 0) {
1571         return array(sprintf(_('Could not untar %s to %s'), $filename, $amp_conf['AMPWEBROOT'].'/admin/modules/'));
1572       }
1573       
1574       // invoke progress_callback
1575       if (function_exists($progress_callback)) {
1576         $progress_callback('done', array('module'=>$modulename));
1577       }
1578       
1579       return true;
1580     } else {
1581       unlink($filename);
1582     }
1583   }
1584  
1585   if (isset($amp_conf['AMPMODULESVN'])) {
1586     $url = $amp_conf['AMPMODULESVN'].$res['location'];
1587     // echo "(From amportal.conf)"; // debug
1588   } else {
1589     $url = "http://mirror.freepbx.org/modules/".$res['location'];
1590     // echo "(From default)"; // debug
1591   }
1592  
1593   if (!($fp = @fopen($filename,"w"))) {
1594     return array(sprintf(_("Error opening %s for writing"), $filename));
1595   }
1596  
1597   $headers = get_headers($url);
1598  
1599   $totalread = 0;
1600   // invoke progress_callback
1601   if (function_exists($progress_callback)) {
1602     $progress_callback('downloading', array('module'=>$modulename, 'read'=>$totalread, 'total'=>$headers['content-length']));
1603   }
1604  
1605   if (!$dp = @fopen($url,'r')) {
1606     return array(sprintf(_("Error opening %s for reading"), $url));
1607   }
1608  
1609   $filedata = '';
1610   while (!feof($dp)) {
1611     $data = fread($dp, $download_chunk_size);
1612     $filedata .= $data;
1613     $totalread += strlen($data);
1614     if (function_exists($progress_callback)) {
1615       $progress_callback('downloading', array('module'=>$modulename, 'read'=>$totalread, 'total'=>$headers['content-length']));
1616     }
1617   }
1618   fwrite($fp,$filedata);
1619   fclose($dp);
1620   fclose($fp);
1621  
1622  
1623   if (is_readable($filename) !== TRUE ) {
1624     return array(sprintf(_('Unable to save %s'),$filename));
1625   }
1626  
1627   // Check the MD5 info against what's in the module's XML
1628   if (!isset($res['md5sum']) || empty($res['md5sum'])) {
1629     //echo "<div class=\"error\">"._("Unable to Locate Integrity information for")." {$filename} - "._("Continuing Anyway")."</div>";
1630   } else if ($res['md5sum'] != md5 ($filedata)) {
1631     unlink($filename);
1632     return array(sprintf(_('File Integrity failed for %s - aborting'), $filename));
1633   }
1634  
1635   // invoke progress callback
1636   if (function_exists($progress_callback)) {
1637     $progress_callback('untar', array('module'=>$modulename, 'size'=>filesize($filename)));
1638   }
1639
1640   exec("tar zxf ".escapeshellarg($filename)." --directory=".escapeshellarg($amp_conf['AMPWEBROOT'].'/admin/modules/'), $output, $exitcode);
1641   if ($exitcode != 0) {
1642     return array(sprintf(_('Could not untar %s to %s'), $filename, $amp_conf['AMPWEBROOT'].'/admin/modules/'));
1643   }
1644  
1645   // invoke progress_callback
1646   if (function_exists($progress_callback)) {
1647     $progress_callback('done', array('module'=>$modulename));
1648   }
1649
1650   return true;
1651 }
1652
1653 /** Installs or upgrades a module from it's directory
1654  * Checks dependencies, and enables
1655  * @param string   The name of the module to install
1656  * @param bool     If true, skips status and dependency checks
1657  * @return mixed   True if succesful, array of error messages if not succesful
1658  */
1659 function module_install($modulename, $force = false) {
1660   $modules = module_getinfo($modulename);
1661   if (($modules[$modulename]['status'] == MODULE_STATUS_NOTINSTALLED) ||
1662       ($modules[$modulename]['status'] == MODULE_STATUS_NEEDUPGRADE)) {
1663   }
1664   global $db, $amp_conf;
1665  
1666   // make sure we have a directory, to begin with
1667   $dir = $amp_conf['AMPWEBROOT'].'/admin/modules/'.$modulename;
1668   if (!is_dir($dir)) {
1669     return array(_("Cannot find module"));
1670   }
1671  
1672   // read the module.xml file
1673   $modules = module_getinfo($modulename);
1674   if (!isset($modules[$modulename])) {
1675     return array(_("Could not read module.xml"));
1676   }
1677  
1678   // don't force this bit - we can't install a broken module (missing files)
1679   if ($modules[$modulename]['status'] == MODULE_STATUS_BROKEN) {
1680     return array(_("This module is broken and cannot be installed. You should try to download it again."));
1681   }
1682  
1683   if (!$force) {
1684  
1685     if (!in_array($modules[$modulename]['status'], array(MODULE_STATUS_NOTINSTALLED, MODULE_STATUS_NEEDUPGRADE))) {
1686       return array(_("This module is already installed."));
1687     }
1688     
1689     // check dependencies
1690     if (is_array($errors = module_checkdepends($modules[$modulename]))) {
1691       return $errors;
1692     }
1693   }
1694  
1695   // run the scripts
1696   if (!_module_runscripts($modulename, 'install')) {
1697     return array(_("Failed to run installation scripts"));
1698   }
1699  
1700   if ($modules[$modulename]['status'] == MODULE_STATUS_NOTINSTALLED) {
1701     // customize INSERT query
1702     switch ($amp_conf["AMPDBENGINE"]) {
1703       case "sqlite":
1704         // to support sqlite2, we are not using autoincrement. we need to find the
1705         // max ID available, and then insert it
1706         $sql = "SELECT max(id) FROM modules;";
1707         $results = $db->getRow($sql);
1708         $new_id = $results[0];
1709         $new_id ++;
1710         $sql = "INSERT INTO modules (id,modulename, version,enabled) values ('".$new_id."','".addslashes($modules[$modulename]['rawname'])."','".addslashes($modules[$modulename]['version'])."','0' );";
1711         break;
1712       
1713       default:
1714         $sql = "INSERT INTO modules (modulename, version, enabled) values ('".addslashes($modules[$modulename]['rawname'])."','".addslashes($modules[$modulename]['version'])."', 1);";
1715       break;
1716     }
1717   } else {
1718     // just need to update the version
1719     $sql = "UPDATE modules SET version='".addslashes($modules[$modulename]['version'])."' WHERE modulename = '".addslashes($modules[$modulename]['rawname'])."'";
1720   }
1721  
1722   // run query
1723   $results = $db->query($sql);
1724   if(DB::IsError($results)) {
1725     return array(sprintf(_("Error updating database. Command was: %s; error was: %s "), $sql, $results->getMessage()));
1726   }
1727  
1728   // module is now installed & enabled
1729   return true;
1730 }
1731
1732 /** Disable a module, but reqmains installed
1733  * @param string   The name of the module to disable
1734  * @param bool     If true, skips status and dependency checks
1735  * @return mixed   True if succesful, array of error messages if not succesful
1736 */
1737 function module_disable($modulename, $force = false) { // was disableModule
1738   $modules = module_getinfo($modulename);
1739   if (!isset($modules[$modulename])) {
1740     return array(_("Specified module not found"));
1741   }
1742  
1743   if (!$force) {
1744     if ($modules[$modulename]['status'] != MODULE_STATUS_ENABLED) {
1745       return array(_("Module not enabled: cannot disable"));
1746     }
1747     
1748     if ( ($depmods = module_reversedepends($modulename)) !== false) {
1749       return array(_("Cannot disable: The following modules depend on this one: ").implode(',',$depmods));
1750     }
1751   }
1752  
1753   _module_setenabled($modulename, false);
1754   return true;
1755 }
1756
1757 /** Uninstall a module, but files remain
1758  * @param string   The name of the module to install
1759  * @param bool     If true, skips status and dependency checks
1760  * @return mixed   True if succesful, array of error messages if not succesful
1761  */
1762 function module_uninstall($modulename, $force = false) {
1763   global $db;
1764  
1765   $modules = module_getinfo($modulename);
1766   if (!isset($modules[$modulename])) {
1767     return array(_("Specified module not found"));
1768   }
1769  
1770   if (!$force) {
1771     if ($modules[$modulename]['status'] == MODULE_STATUS_NOTINSTALLED) {
1772       return array(_("Module not installed: cannot uninstall"));
1773     }
1774     
1775     if ( ($depmods = module_reversedepends($modulename)) !== false) {
1776       return array(_("Cannot disable: The following modules depend on this one: ").implode(',',$depmods));
1777     }
1778   }
1779  
1780   $sql = "DELETE FROM modules WHERE modulename = '".addslashes($modulename)."'";
1781   $results = $db->query($sql);
1782   if(DB::IsError($results)) {
1783     return array(_("Error updating database: ").$results->getMessage());
1784   }
1785  
1786   if (!_module_runscripts($modulename, 'uninstall')) {
1787     return array(_("Failed to run un-installation scripts"));
1788   }
1789  
1790   return true;
1791 }
1792
1793 /** Totally deletes a module
1794  * @param string   The name of the module to install
1795  * @param bool     If true, skips status and dependency checks
1796  * @return mixed   True if succesful, array of error messages if not succesful
1797  */
1798 function module_delete($modulename, $force = false) {
1799   global $amp_conf;
1800  
1801   $modules = module_getinfo($modulename);
1802   if (!isset($modules[$modulename])) {
1803     return array(_("Specified module not found"));
1804   }
1805  
1806   if ($modules[$modulename]['status'] != MODULE_STATUS_NOTINSTALLED) {
1807     if (is_array($errors = module_uninstall($modulename, $force))) {
1808       return $errors;
1809     }
1810   }
1811  
1812   // delete module directory
1813   //TODO : do this in pure php
1814   $dir = $amp_conf['AMPWEBROOT'].'/admin/modules/'.$modulename;
1815   if (!is_dir($dir)) {
1816     return array(sprintf(_("Cannot delete directory %s"), $dir));
1817   }
1818   if (strpos($dir,"..") !== false) {
1819     die("Security problem, denying delete");
1820   }
1821   exec("rm -r ".escapeshellarg($dir),$output, $exitcode);
1822   if ($exitcode != 0) {
1823     return array(sprintf(_("Error deleting directory %s (code %d)"), $dir, $exitcode));
1824   }
1825  
1826   return true;
1827 }
1828
1829
1830 /** Internal use only */
1831 function _module_setenabled($modulename, $enabled) {
1832   global $db;
1833   $sql = 'UPDATE modules SET enabled = '.($enabled ? '1' : '0').' WHERE modulename = "'.addslashes($modulename).'"';
1834   $results = $db->query($sql);
1835   if(DB::IsError($results)) {
1836     die($results->getMessage());
1837   }
1838 }
1839
1840 function _module_readxml($modulename) {
1841   global $amp_conf;
1842   $dir = $amp_conf['AMPWEBROOT'].'/admin/modules/'.$modulename;
1843   if (is_dir($dir) && file_exists($dir.'/module.xml')) {
1844     $data = file_get_contents($dir.'/module.xml');
1845     //$parser = new xml2ModuleArray($data);
1846     //$xmlarray = $parser->parseModulesXML($data);
1847     $parser = new xml2Array($data);
1848     $xmlarray = $parser->parseAdvanced($data);
1849     if (isset($xmlarray['module'])) {
1850       // add a couple fields first
1851       $xmlarray['module']['displayname'] = $xmlarray['module']['name'];
1852       if (isset($xmlarray['module']['menuitems'])) {
1853         $xmlarray['module']['items'] = $xmlarray['module']['menuitems'];
1854       }
1855       return $xmlarray['module'];
1856     }
1857   }
1858   return null;
1859 }
1860
1861 // Temporarily copied here, for people that haven't upgraded their
1862 // IVR module..
1863
1864 function modules_getversion($modname) {
1865   return _modules_getversion($modname);
1866 }
1867
1868 // This returns the version of a module
1869 function _modules_getversion($modname) {
1870   global $db;
1871
1872   $sql = "SELECT version FROM modules WHERE modulename = '".addslashes($modname)."'";
1873   $results = $db->getRow($sql,DB_FETCHMODE_ASSOC);
1874   if (isset($results['version']))
1875     return $results['version'];
1876   else
1877     return null;
1878 }
1879
1880 /** Updates the version field in the module table
1881  * Should only be called internally
1882  */
1883 function _modules_setversion($modname, $vers) {
1884   global $db;
1885
1886   return ;
1887 }
1888
1889 /** Run the module install/uninstall scripts
1890  * @param string  The name of the module
1891  * @param string  The action to perform, either 'install' or 'uninstall'
1892  * @return boolean  If the action was succesful
1893  */
1894 function _module_runscripts($modulename, $type) {
1895   global $amp_conf;
1896   $db_engine = $amp_conf["AMPDBENGINE"];
1897  
1898   $moduledir = $amp_conf["AMPWEBROOT"]."/admin/modules/".$modulename;
1899   if (!is_dir($moduledir)) {
1900     return false;
1901   }
1902  
1903   switch ($type) {
1904     case 'install':
1905       // install sql files
1906       if ($db_engine  == "sqlite") {
1907         $sqlfilename = "install.sqlite";
1908       } else {
1909         $sqlfilename = "install.sql";
1910       }
1911       
1912       if (is_file($moduledir.'/'.$sqlfilename)) {
1913         execSQL($moduledir.'/'.$sqlfilename);
1914       }
1915       
1916       // then run .php scripts
1917       _modules_doinclude($moduledir.'/install.php', $modulename);
1918     break;
1919     case 'uninstall':
1920       // run uninstall .php scripts first
1921       _modules_doinclude($moduledir.'/uninstall.php', $modulename);
1922       
1923       if ($db_engine  == "sqlite") {
1924         $sqlfilename = "uninstall.sqlite";
1925       } else {
1926         $sqlfilename = "uninstall.sql";
1927       }
1928       
1929       // then uninstall sql files
1930       if (is_file($moduledir.'/'.$sqlfilename)) {
1931         execSQL($moduledir.'/'.$sqlfilename);
1932       }
1933       
1934     break;
1935     default:
1936       return false;
1937   }
1938  
1939   return true;
1940 }
1941 function _modules_doinclude($filename, $modulename) {
1942   // we provide the following variables to the included file (as well as $filename and $modulename)
1943   global $db, $amp_conf, $asterisk_conf;
1944  
1945   if (file_exists($filename) && is_file($filename)) {
1946     include($filename);
1947   }
1948 }
1949  
1950
1951 /*
1952 function installModule($modname,$modversion)
1953 {
1954   global $db;
1955   global $amp_conf;
1956  
1957   switch ($amp_conf["AMPDBENGINE"])
1958   {
1959     case "sqlite":
1960       // to support sqlite2, we are not using autoincrement. we need to find the
1961       // max ID available, and then insert it
1962       $sql = "SELECT max(id) FROM modules;";
1963       $results = $db->getRow($sql);
1964       $new_id = $results[0];
1965       $new_id ++;
1966       $sql = "INSERT INTO modules (id,modulename, version,enabled) values ('{$new_id}','{$modname}','{$modversion}','0' );";
1967       break;
1968     
1969     default:
1970       $sql = "INSERT INTO modules (modulename, version) values ('{$modname}','{$modversion}');";
1971     break;
1972   }
1973
1974   $results = $db->query($sql);
1975   if(DB::IsError($results)) {
1976     die($results->getMessage());
1977   }
1978 }
1979
1980 function uninstallModule($modname) {
1981   global $db;
1982   $sql = "DELETE FROM modules WHERE modulename = '{$modname}'";
1983   $results = $db->query($sql);
1984   if(DB::IsError($results)) {
1985     die($results->getMessage());
1986   }
1987 }
1988
1989 /** downloads a module, and extracts it into the module dir
1990  * /
1991 function module_fetch($name) { // was fetchModule
1992   global $amp_conf;
1993   $res = module_getonlinexml($modulename);
1994   if (!isset($res)) {
1995     echo "<div class=\"error\">"._("Unaware of module")." {$name}</div>";
1996     return false;
1997   }
1998   $file = basename($res['location']);
1999   $filename = $amp_conf['AMPWEBROOT']."/admin/modules/_cache/".$file;
2000   if(file_exists($filename)) {
2001     // We might already have it! Let's check the MD5.
2002     $filedata = "";
2003     $fh = @fopen($filename, "r");
2004     while (!feof($fh)) {
2005       $filedata .= fread($fh, 8192);
2006     }
2007     if (isset($res['md5sum']) && $res['md5sum'] == md5 ($filedata)) {
2008       // Note, if there's no MD5 information, it will redownload
2009       // every time. Otherwise theres no way to avoid a corrupt
2010       // download
2011       
2012       return verifyAndInstall($filename);
2013     } else {
2014       unlink($filename);
2015     }
2016   }
2017   if (isset($amp_conf['AMPMODULESVN'])) {
2018     $url = $amp_conf['AMPMODULESVN'].$res['location'];
2019     // echo "(From amportal.conf)"; // debug
2020   } else {
2021   $url = "http://mirror.freepbx.org/modules/".$res['location'];
2022     // echo "(From default)"; // debug
2023   }
2024   $fp = @fopen($filename,"w");
2025   $filedata = file_get_contents($url);
2026   fwrite($fp,$filedata);
2027   fclose($fp);
2028   if (is_readable($filename) !== TRUE ) {
2029     echo "<div class=\"error\">"._("Unable to save")." {$filename} - Check file/directory permissions</div>";
2030     return false;
2031   }
2032   // Check the MD5 info against what's in the module's XML
2033   if (!isset($res['md5sum']) || empty($res['md5sum'])) {
2034     echo "<div class=\"error\">"._("Unable to Locate Integrity information for")." {$filename} - "._("Continuing Anyway")."</div>";
2035   } elseif ($res['md5sum'] != md5 ($filedata)) {
2036     echo "<div class=\"error\">"._("File Integrity FAILED for")." {$filename} - "._("Aborting")."</div>";
2037     unlink($filename);
2038     return false;
2039   }
2040   // verifyAndInstall does the untar, and will do the signed-package check.
2041   return verifyAndInstall($filename);
2042
2043 }
2044
2045 function upgradeModule($module, $allmods = NULL) {
2046   if($allmods === NULL)
2047     $allmods = find_allmodules();
2048   // the install.php can set this to false if the upgrade fails.
2049   $success = true;
2050   if(is_file("modules/$module/install.php"))
2051     include "modules/$module/install.php";
2052   if ($success) {
2053     sql('UPDATE modules SET version = "'.$allmods[$module]['version'].'" WHERE modulename = "'.$module.'"');
2054     needreload();
2055   }
2056 }
2057
2058 function rmModule($module) {
2059   global $amp_conf;
2060   if($module != 'core') {
2061     if (is_dir($amp_conf['AMPWEBROOT'].'/admin/modules/'.$module) && strstr($module, '.') === FALSE ) {
2062       exec('/bin/rm -rf '.$amp_conf['AMPWEBROOT'].'/admin/modules/'.$module);
2063     }
2064   } else {
2065     echo "<script language=\"Javascript\">alert('"._("You cannot delete the Core module")."');</script>";
2066   }
2067 }
2068
2069 */
2070
2071
2072 function freepbx_log($section, $level, $message) {
2073         global $db;
2074         global $debug; // This is used by retrieve_conf
2075
2076         $sth = $db->prepare("INSERT INTO freepbx_log (time, section, level, message) VALUES (NOW(),?,?,?)");
2077         $db->execute($sth, array($section, $level, $message));
2078         if (isset($debug) && ($debug != false))
2079                 print "[DEBUG-$section] ($level) $message\n";
2080 }
2081 ?>
Note: See TracBrowser for help on using the browser.