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

Revision 3183, 68.4 kB (checked in by gregmac, 7 years ago)

Add freepbx_log() description from 2.2

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