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

Revision 2643, 47.7 kB (checked in by gregmac, 7 years ago)

Beginning of modules revamp: moving module_* api functions to functions.inc.php (see ApiModules);
Redesigning modules page to be more category-centric (currently upgrades/installs/etc are broken)

  • 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('featurecodes.class.php');
15 require_once('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 /*           
44     if (($amp_conf["AMPDBENGINE"] == "sqlite") && (!isset($amp_conf["AMPDBENGINE"])))
45         $amp_conf["AMPDBFILE"] = "/var/lib/freepbx/freepbx.sqlite";
46 */
47     
48     return $conf;
49 }
50
51 function parse_asterisk_conf($filename) {
52     $file = file($filename);
53     foreach ($file as $line) {
54         if (preg_match("/^\s*([a-zA-Z0-9]+)\s* => \s*(.*)\s*([;#].*)?/",$line,$matches)) {
55             $conf[ $matches[1] ] = $matches[2];
56         }
57     }
58     return $conf;
59 }
60
61 function getAmpAdminUsers() {
62     global $db;
63
64     $sql = "SELECT username FROM ampusers WHERE sections='*'";
65     $results = $db->getAll($sql);
66     if(DB::IsError($results)) {
67        die($results->getMessage());
68     }
69     return $results;
70 }
71
72 function getAmpUser($username) {
73     global $db;
74     
75     $sql = "SELECT username, password, extension_low, extension_high, deptname, sections FROM ampusers WHERE username = '".addslashes($username)."'";
76     $results = $db->getAll($sql);
77     if(DB::IsError($results)) {
78        die($results->getMessage());
79     }
80     
81     if (count($results) > 0) {
82         $user = array();
83         $user["username"] = $results[0][0];
84         $user["password"] = $results[0][1];
85         $user["extension_low"] = $results[0][2];
86         $user["extension_high"] = $results[0][3];
87         $user["deptname"] = $results[0][4];
88         $user["sections"] = explode(";",$results[0][5]);
89         return $user;
90     } else {
91         return false;
92     }
93 }
94
95 class ampuser {
96     var $username;
97     var $_password;
98     var $_extension_high;
99     var $_extension_low;
100     var $_deptname;
101     var $_sections;
102     
103     function ampuser($username) {
104         $this->username = $username;
105         if ($user = getAmpUser($username)) {
106             $this->_password = $user["password"];
107             $this->_extension_high = $user["extension_high"];
108             $this->_extension_low = $user["extension_low"];
109             $this->_deptname = $user["deptname"];
110             $this->_sections = $user["sections"];
111         } else {
112             // user doesn't exist
113             $this->_password = false;
114             $this->_extension_high = "";
115             $this->_extension_low = "";
116             $this->_deptname = "";
117             $this->_sections = array();
118         }
119     }
120     
121     /** Give this user full admin access
122     */
123     function setAdmin() {
124         $this->_extension_high = "";
125         $this->_extension_low = "";
126         $this->_deptname = "";
127         $this->_sections = array("*");
128     }
129     
130     function checkPassword($password) {
131         // strict checking so false will never match
132         return ($this->_password === $password);
133     }
134     
135     function checkSection($section) {
136         // if they have * then it means all sections
137         return in_array("*", $this->_sections) || in_array($section, $this->_sections);
138     }
139 }
140
141 // returns true if extension is within allowed range
142 function checkRange($extension){
143     $low = isset($_SESSION["AMP_user"]->_extension_low)?$_SESSION["AMP_user"]->_extension_low:'';
144     $high = isset($_SESSION["AMP_user"]->_extension_high)?$_SESSION["AMP_user"]->_extension_high:'';
145     
146     if ((($extension >= $low) && ($extension <= $high)) || ($low == '' && $high == ''))
147         return true;
148     else
149         return false;
150 }
151
152 // returns true if department string matches dept for this user
153 function checkDept($dept){
154     $deptname = isset($_SESSION["AMP_user"])?$_SESSION["AMP_user"]->_deptname:null;
155     
156     if ( ($dept == null) || ($dept == $deptname) )
157         return true;
158     else
159         return false;
160 }
161
162 /* look for all modules in modules dir.
163 ** returns array:
164 ** array['module']['displayName']
165 ** array['module']['version']
166 ** array['module']['type']
167 ** array['module']['status']
168 ** array['module']['items'][array(items)]
169 ** Use find_modules() to return only specific type or status
170 */
171 function find_allmodules() {
172     global $db;
173     global $amp_conf;
174     
175     if (!is_dir($amp_conf['AMPWEBROOT'].'/admin/modules'))
176     {
177         mkdir( $amp_conf['AMPWEBROOT'].'/admin/modules' );
178         return;
179     }
180     
181     $dir = opendir($amp_conf['AMPWEBROOT'].'/admin/modules');
182     $data = "<xml>";
183     //loop through each module directory, ensure there is a module.ini file
184     while ($file = readdir($dir)) {
185         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')) {
186             //open module.xml and read contents
187             if(is_file($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file.'/module.xml')){
188                 $data .=file_get_contents($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file.'/module.xml');
189                 
190             }
191         }
192     }
193     $data .= "</xml>";
194     $parser = new xml2ModuleArray($data);
195     $xmlarray = $parser->parseModulesXML($data);
196     
197     // determine details about this module from database
198     // modulename should match the directory name
199     $sql = "SELECT * FROM modules";
200     $results = $db->getAll($sql,DB_FETCHMODE_ASSOC);
201     if(DB::IsError($results)) {
202         die($results->getMessage());
203     }
204     
205     if (is_array($results)) {
206         foreach($results as $result) {
207                 /*
208                 set status key based on results
209                 -1=broken (in table, not not on filesystem)
210                 0 or null=not installed
211                 1=disabled
212                 2=enabled
213                 3=enabled and needs upgrade
214                 */
215                 if(isset($xmlarray[ $result['modulename'] ] ) && is_array($xmlarray[ $result['modulename'] ])) {
216                     if ($result['enabled'] != 0) {
217                         // check if file and registered versions are the same
218                         // version_compare returns 0 if no difference
219                         if (version_compare($result['version'],$xmlarray[ $result['modulename'] ]["version"]) === 0)
220                             $xmlarray[ $result['modulename'] ]["status"] = 2;
221                         else
222                             $xmlarray[ $result['modulename'] ]["status"] = 3;
223                     } else {
224                         $xmlarray[ $result['modulename'] ]["status"] = 1;
225                     }
226                 } else {
227                     $xmlarray[ $result['modulename'] ]["status"] = -1;
228                 }
229                     
230         }
231     }
232
233     //echo "<pre>"; print_r($xmlarray); echo "</pre>";
234     return $xmlarray;
235 }
236
237 /* finds modules of the specified status and type
238 ** $status can be 0 (not installed), 1 (disabled), 2 (enabled)
239 ** $type can be 'setup' or 'tool'
240 **
241 ** returns array:
242 ** array['module']['displayName']
243 ** array['module']['version']
244 ** array['module']['type']
245 ** array['module']['status']
246 ** array['module']['items'][array(items)]
247 */
248 function find_modules($status) {
249     $modules = find_allmodules();
250     if (isset($modules) && is_array($modules)) {   
251         foreach(array_keys($modules) as $key) {
252             //remove modules not matching status
253             if(isset($modules[$key]['status']) && $modules[$key]['status'] == $status ){
254                 $return_modules[$key] = $modules[$key];
255             }
256         }
257         return $return_modules;
258     } else {
259         return false;
260     }
261 }
262
263
264 // This returns the version of a module
265 function modules_getversion($modname) {
266     global $db;
267
268     $sql = "SELECT version FROM modules WHERE modulename = '".addslashes($modname)."'";
269     $results = $db->getRow($sql,DB_FETCHMODE_ASSOC);
270     if (isset($results['version']))
271         return $results['version'];
272     else
273         return null;
274 }
275
276 // I bet you can't guess what this one does.
277 function modules_setversion($modname, $vers) {
278     global $db;
279
280     return sql("UPDATE modules SET version='".addslashes($vers)."' WHERE modulename = '".addslashes($modname)."'");
281 }
282
283
284 /* queries database using PEAR.
285 *  $type can be query, getAll, getRow, getCol, getOne, etc
286 *  $fetchmode can be DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
287 *  returns array, unless using getOne
288 */
289 function sql($sql,$type="query",$fetchmode=null) {
290     global $db;
291     $results = $db->$type($sql,$fetchmode);
292     if(DB::IsError($results)) {
293         die($results->getDebugInfo());
294     }
295     return $results;
296 }
297
298 // sql text formatting -- couldn't see that one was available already
299 function sql_formattext($txt) {
300     if (isset($txt)) {
301         $fmt = str_replace("'", "''", $txt);
302         $fmt = "'" . $fmt . "'";
303     } else {
304         $fmt = 'null';
305     }
306
307     return $fmt;
308 }
309
310 //tell application we need to reload asterisk
311 function needreload() {
312     global $db;
313     $sql = "UPDATE admin SET value = 'true' WHERE variable = 'need_reload'";
314     $result = $db->query($sql);
315     if(DB::IsError($result)) {     
316         die($result->getMessage());
317     }
318 }
319
320 //get the version number
321 function getversion() {
322     global $db;
323     $sql = "SELECT value FROM admin WHERE variable = 'version'";
324     $results = $db->getAll($sql);
325     if(DB::IsError($results)) {
326         die($results->getMessage());
327     }
328     return $results;
329 }
330
331 // draw list for users and devices with paging
332 function drawListMenu($results, $skip, $type, $dispnum, $extdisplay, $description) {
333     $perpage=20;
334     
335     $skipped = 0;
336     $index = 0;
337     if ($skip == "") $skip = 0;
338      echo "<li><a id=\"".($extdisplay=='' ? 'current':'')."\" href=\"config.php?type=".$type."&display=".$dispnum."\">"._("Add")." ".$description."</a></li>";
339
340     if (isset($results)) {
341         foreach ($results AS $key=>$result) {
342             if ($index >= $perpage) {
343                 $shownext= 1;
344                 break;
345             }
346             
347             if ($skipped<$skip && $skip!= 0) {
348                 $skipped= $skipped + 1;
349                 continue;
350             }
351             
352             $index= $index + 1;   
353             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>";
354         }
355     }
356     
357     if ($index >= $perpage) {
358          print "<li><center>";
359     }
360     
361     if ($skip) {
362          $prevskip= $skip - $perpage;
363          if ($prevskip<0) $prevskip= 0;
364          $prevtag_pre= "<a href='?type=".$type."&display=".$dispnum."&skip=$prevskip'>[PREVIOUS]</a>";
365          print "$prevtag_pre";
366     }
367     
368     if (isset($shownext)) {
369         $nextskip= $skip + $index;
370         if ($prevtag_pre) $prevtag .= " | ";
371         print "$prevtag <a href='?type=".$type."&display=".$dispnum."&skip=$nextskip'>[NEXT]</a>";
372     }
373     elseif ($skip) {
374         print "$prevtag";
375     }
376     
377      print "</center></li>";
378 }
379
380 // this function simply makes a connection to the asterisk manager, and should be called by modules that require it (ie: dbput/dbget)
381 function checkAstMan() {
382     require_once('common/php-asmanager.php');
383     global $amp_conf;
384     $astman = new AGI_AsteriskManager();
385     if ($res = $astman->connect("127.0.0.1", $amp_conf["AMPMGRUSER"] , $amp_conf["AMPMGRPASS"])) {
386         return $astman->disconnect();
387     } else {
388         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>";
389         exit;
390     }
391 }
392
393
394 /** Recursively read voicemail.conf (and any included files)
395  * This function is called by getVoicemailConf()
396  */
397 function parse_voicemailconf($filename, &$vmconf, &$section) {
398     if (is_null($vmconf)) {
399         $vmconf = array();
400     }
401     if (is_null($section)) {
402         $section = "general";
403     }
404     
405     if (file_exists($filename)) {
406         $fd = fopen($filename, "r");
407         while ($line = fgets($fd, 1024)) {
408             if (preg_match("/^\s*(\d+)\s*=>\s*(\d*),(.*),(.*),(.*),(.*)\s*([;#].*)?/",$line,$matches)) {
409                 // "mailbox=>password,name,email,pager,options"
410                 // this is a voicemail line   
411                 $vmconf[$section][ $matches[1] ] = array("mailbox"=>$matches[1],
412                                     "pwd"=>$matches[2],
413                                     "name"=>$matches[3],
414                                     "email"=>$matches[4],
415                                     "pager"=>$matches[5],
416                                     "options"=>array(),
417                                     );
418                                 
419                 // parse options
420                 //output($matches);
421                 foreach (explode("|",$matches[6]) as $opt) {
422                     $temp = explode("=",$opt);
423                     //output($temp);
424                     if (isset($temp[1])) {
425                         list($key,$value) = $temp;
426                         $vmconf[$section][ $matches[1] ]["options"][$key] = $value;
427                     }
428                 }
429             } else if (preg_match("/^\s*(\d+)\s*=>\s*dup,(.*)\s*([;#].*)?/",$line,$matches)) {
430                 // "mailbox=>dup,name"
431                 // duplace name line
432                 $vmconf[$section][ $matches[1] ]["dups"][] = $matches[2];
433             } else if (preg_match("/^\s*#include\s+(.*)\s*([;#].*)?/",$line,$matches)) {
434                 // include another file
435                 
436                 if ($matches[1][0] == "/") {
437                     // absolute path
438                     $filename = $matches[1];
439                 } else {
440                     // relative path
441                     $filename dirname($filename)."/".$matches[1];
442                 }
443                 
444                 parse_voicemailconf($filename, $vmconf, $section);
445                 
446             } else if (preg_match("/^\s*\[(.+)\]/",$line,$matches)) {
447                 // section name
448                 $section = strtolower($matches[1]);
449             } else if (preg_match("/^\s*([a-zA-Z0-9-_]+)\s*=\s*(.*?)\s*([;#].*)?$/",$line,$matches)) {
450                 // name = value
451                 // option line
452                 $vmconf[$section][ $matches[1] ] = $matches[2];
453             }
454         }
455         fclose($fd);
456     }
457 }
458
459 /** Write the voicemail.conf file
460  * This is called by saveVoicemail()
461  * It's important to make a copy of $vmconf before passing it. Since this is a recursive function, has to
462  * pass by reference. At the same time, it removes entries as it writes them to the file, so if you don't have
463  * a copy, by the time it's done $vmconf will be empty.
464 */
465 function write_voicemailconf($filename, &$vmconf, &$section, $iteration = 0) {
466     if ($iteration == 0) {
467         $section = null;
468     }
469     
470     $output = array();
471         
472     // if the file does not, copy if from the template.
473     // TODO: is this logical?
474     // TODO: don't use hardcoded path...?
475     if (!file_exists($filename)) {
476         if (!copy( "/etc/asterisk/voicemail.conf.template", $filename )){
477             return;
478         }
479     }
480     
481         $fd = fopen($filename, "r");
482         while ($line = fgets($fd, 1024)) {
483             if (preg_match("/^(\s*)(\d+)(\s*)=>(\s*)(\d*),(.*),(.*),(.*),(.*)(\s*[;#].*)?$/",$line,$matches)) {
484                 // "mailbox=>password,name,email,pager,options"
485                 // this is a voicemail line
486                 //DEBUG echo "\nmailbox";
487                 
488                 // make sure we have something as a comment
489                 if (!isset($matches[10])) {
490                     $matches[10] = "";
491                 }
492                 
493                 // $matches[1] [3] and [4] are to preserve indents/whitespace, we add these back in
494                 
495                 if (isset($vmconf[$section][ $matches[2] ])) {   
496                     // we have this one loaded
497                     // repopulate from our version
498                     $temp = & $vmconf[$section][ $matches[2] ];
499                     
500                     $options = array();
501                     foreach ($temp["options"] as $key=>$value) {
502                         $options[] = $key."=".$value;
503                     }
504                     
505                     $output[] = $matches[1].$temp["mailbox"].$matches[3]."=>".$matches[4].$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options).$matches[10];
506                     
507                     // remove this one from $vmconf
508                     unset($vmconf[$section][ $matches[2] ]);
509                 } else {
510                     // we don't know about this mailbox, so it must be deleted
511                     // (and hopefully not JUST added since we did read_voiceamilconf)
512                     
513                     // do nothing
514                 }
515                 
516             } else if (preg_match("/^(\s*)(\d+)(\s*)=>(\s*)dup,(.*)(\s*[;#].*)?$/",$line,$matches)) {
517                 // "mailbox=>dup,name"
518                 // duplace name line
519                 // leave it as-is (for now)
520                 //DEBUG echo "\ndup mailbox";
521                 $output[] = $line;
522             } else if (preg_match("/^(\s*)#include(\s+)(.*)(\s*[;#].*)?$/",$line,$matches)) {
523                 // include another file
524                 //DEBUG echo "\ninclude ".$matches[3]."<blockquote>";
525                 
526                 // make sure we have something as a comment
527                 if (!isset($matches[4])) {
528                     $matches[4] = "";
529                 }
530                 
531                 if ($matches[3][0] == "/") {
532                     // absolute path
533                     $include_filename = $matches[3];
534                 } else {
535                     // relative path
536                     $include_filename dirname($filename)."/".$matches[3];
537                 }
538                 
539                 $output[] = $matches[1]."#include".$matches[2].$matches[3].$matches[4];
540                 write_voicemailconf($include_filename, $vmconf, $section, $iteration+1);
541                 
542                 //DEBUG echo "</blockquote>";
543                 
544             } else if (preg_match("/^(\s*)\[(.+)\](\s*[;#].*)?$/",$line,$matches)) {
545                 // section name
546                 //DEBUG echo "\nsection";
547                 
548                 // make sure we have something as a comment
549                 if (!isset($matches[3])) {
550                     $matches[3] = "";
551                 }
552                 
553                 // check if this is the first run (section is null)
554                 if ($section !== null) {
555                     // we need to add any new entries here, before the section changes
556                     //DEBUG echo "<blockquote><i>";
557                     //DEBUG var_dump($vmconf[$section]);
558                     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
559                         foreach ($vmconf[$section] as $key=>$value) {
560                             if (is_array($value)) {
561                                 // mailbox line
562                                 
563                                 $temp = & $vmconf[$section][ $key ];
564                                 
565                                 $options = array();
566                                 foreach ($temp["options"] as $key1=>$value) {
567                                     $options[] = $key1."=".$value;
568                                 }
569                                 
570                                 $output[] = $temp["mailbox"]." => ".$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options);
571                                 
572                                 // remove this one from $vmconf
573                                 unset($vmconf[$section][ $key ]);
574                                 
575                             } else {
576                                 // option line
577                                 
578                                 $output[] = $key."=".$vmconf[$section][ $key ];
579                                 
580                                 // remove this one from $vmconf
581                                 unset($vmconf[$section][ $key ]);
582                             }
583                         }
584                     }
585                     //DEBUG echo "</i></blockquote>";
586                 }
587                 
588                 $section = strtolower($matches[2]);
589                 $output[] = $matches[1]."[".$section."]".$matches[3];
590                 $existing_sections[] = $section; //remember that this section exists
591
592             } else if (preg_match("/^(\s*)([a-zA-Z0-9-_]+)(\s*)=(\s*)(.*?)(\s*[;#].*)?$/",$line,$matches)) {
593                 // name = value
594                 // option line
595                 //DEBUG echo "\noption line";
596                 
597                 
598                 // make sure we have something as a comment
599                 if (!isset($matches[6])) {
600                     $matches[6] = "";
601                 }
602                 
603                 if (isset($vmconf[$section][ $matches[2] ])) {
604                     $output[] = $matches[1].$matches[2].$matches[3]."=".$matches[4].$vmconf[$section][ $matches[2] ].$matches[6];
605                     
606                     // remove this one from $vmconf
607                     unset($vmconf[$section][ $matches[2] ]);
608                 }
609                 // else it's been deleted, so we don't write it in
610                 
611             } else {
612                 // unknown other line -- probably a comment or whitespace
613                 //DEBUG echo "\nother: ".$line;
614                 
615                 $output[] = str_replace(array("\n","\r"),"",$line); // str_replace so we don't double-space
616             }
617         }
618         
619         if (($iteration == 0) && (is_array($vmconf))) {
620             // we need to add any new entries here, since it's the end of the file
621             //DEBUG echo "END OF FILE!! <blockquote><i>";
622             //DEBUG var_dump($vmconf);
623             foreach (array_keys($vmconf) as $section) {
624                 if (!in_array($section,$existing_sections))  // If this is a new section, write the context label
625                     $output[] = "[".$section."]";
626                 foreach ($vmconf[$section] as $key=>$value) {
627                     if (is_array($value)) {
628                         // mailbox line
629                         
630                         $temp = & $vmconf[$section][ $key ];
631                         
632                         $options = array();
633                         foreach ($temp["options"] as $key=>$value) {
634                             $options[] = $key."=".$value;
635                         }
636                         
637                         $output[] = $temp["mailbox"]." => ".$temp["pwd"].",".$temp["name"].",".$temp["email"].",".$temp["pager"].",". implode("|",$options);
638                         
639                         // remove this one from $vmconf
640                         unset($vmconf[$section][ $key ]);
641                         
642                     } else {
643                         // option line
644                         
645                         $output[] = $key."=".$vmconf[$section][ $key ];
646                         
647                         // remove this one from $vmconf
648                         unset($vmconf[$section][$key ]);
649                     }
650                 }
651             }
652             //DEBUG echo "</i></blockquote>";
653         }
654         
655         fclose($fd);
656         
657         //DEBUG echo "\n\nwriting ".$filename;
658         //DEBUG echo "\n-----------\n";
659         //DEBUG echo implode("\n",$output);
660         //DEBUG echo "\n-----------\n";
661         
662         // write this file back out
663         
664         if ($fd = fopen($filename, "w")) {
665             fwrite($fd, implode("\n",$output)."\n");
666             fclose($fd);
667         }
668         
669 }
670
671
672 // $goto is the current goto destination setting
673 // $i is the destination set number (used when drawing multiple destination sets in a single form ie: digital receptionist)
674 // esnure that any form that includes this calls the setDestinations() javascript function on submit.
675 // ie: if the form name is "edit", and drawselects has been called with $i=2 then use onsubmit="setDestinations(edit,2)"
676 function drawselects($goto,$i) { 
677     
678     /* --- MODULES BEGIN --- */
679     global $active_modules;
680     
681     // This is purely to remove a warning.
682     if (!isset($selectHtml)) { $selectHtml=''; }
683     $selectHtml .= '<tr><td colspan=2>';
684     
685     //check for module-specific destination functions
686     foreach ($active_modules as $mod => $displayname) {
687         $funct = strtolower($mod.'_destinations');
688     
689         //if the modulename_destinations() function exits, run it and display selections for it
690         if (function_exists($funct)) {
691             $options = "";
692             $destArray = $funct(); //returns an array with 'destination' and 'description'.
693             $checked = false;
694             if (isset($destArray)) {
695                 //loop through each option returned by the module
696                 foreach ($destArray as $dest) {
697                     // check to see if the currently selected goto matches one these destinations
698                     if ($dest['destination'] == $goto)
699                         $checked = true//there is a match, so we select the radio for this group
700
701                     // create an select option for each destination
702                     $options .= '<option value="'.$dest['destination'].'" '.(strpos($goto,$dest['destination']) === false ? '' : 'SELECTED').'>'.($dest['description'] ? $dest['description'] : $dest['destination']);
703                 }
704                 
705                 // make a unique id to be used for the HTML id
706                 // This allows us to have multiple drawselect() sets on the page without
707                 // conflicting with each other
708                 $radioid = uniqid("drawselect");
709                 
710                 $selectHtml .=    '<input type="radio" id="'.$radioid.'" name="goto_indicate'.$i.'" value="'.$mod.'" onclick="javascript:this.form.goto'.$i.'.value=\''.$mod.'\';" onkeypress="javascript:if (event.keyCode == 0 || (document.all && event.keyCode == 13)) this.form.goto'.$i.'.value=\''.$mod.'\';" '.($checked? 'CHECKED=CHECKED' : '').' /> '._($displayname['displayName']).': ';
711                 if ($checked) { $goto = $mod; }
712                 $selectHtml .=    '<select name="'.$mod.$i.'" onfocus="document.getElementById(\''.$radioid.'\').checked = true;">';
713                 $selectHtml .= $options;   
714                 $selectHtml .=    "</select><br>\n";
715             }
716             
717         }
718     }
719     /* --- MODULES END --- */
720     
721     //display a custom goto field
722     $radioid = uniqid("drawselect");
723     $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').' />';
724     $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>:';
725     $selectHtml .= '<input type="text" size="15" name="custom'.$i.'" value="'.(strpos($goto,'custom') === false ? '' : $goto).'" onfocus="document.getElementById(\''.$radioid.'\').checked = true;" />';
726     $selectHtml .= "\n<input type='hidden' name='goto$i' value='$goto'>";
727
728     //close off our row
729     $selectHtml .= '</td></tr>';
730     
731     return $selectHtml;
732 }
733
734
735 /* below are legacy functions required to allow pre 2.0 modules to function (ie: interact with 'extensions' table) */
736
737     //add to extensions table - used in callgroups.php
738     function legacy_extensions_add($addarray) {
739         global $db;
740         $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]."')";
741         $result = $db->query($sql);
742         if(DB::IsError($result)) {
743             die($result->getMessage().$sql);
744         }
745         return $result;
746     }
747     
748     //delete extension from extensions table
749     function legacy_extensions_del($context,$exten) {
750         global $db;
751         $sql = "DELETE FROM extensions WHERE context = '".addslashes($context)."' AND `extension` = '".addslashes($exten)."'";
752         $result = $db->query($sql);
753         if(DB::IsError($result)) {
754             die($result->getMessage());
755         }
756         return $result;
757     }
758     
759     
760     //get args for specified exten and priority - primarily used to grab goto destination
761     function legacy_args_get($exten,$priority,$context) {
762         global $db;
763         $sql = "SELECT args FROM extensions WHERE extension = '".addslashes($exten)."' AND priority = '".addslashes($priority)."' AND context = '".addslashes($context)."'";
764         list($args) = $db->getRow($sql);
765         return $args;
766     }
767
768 /* end legacy functions */
769
770 /* Usage
771 Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML;
772
773 $objXML = new xml2Array();
774 $arrOutput = $objXML->parse($strYourXML);
775 print_r($arrOutput); //print it out, or do whatever!
776
777 */
778
779 class xml2Array {
780     
781     var $arrOutput = array();
782     var $resParser;
783     var $strXmlData;
784     
785     function parse($strInputXML) {
786     
787             $this->resParser = xml_parser_create ();
788             xml_set_object($this->resParser,$this);
789             xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
790             
791             xml_set_character_data_handler($this->resParser, "tagData");
792         
793             $this->strXmlData = xml_parse($this->resParser,$strInputXML );
794             if(!$this->strXmlData) {
795                 die(sprintf("XML error: %s at line %d",
796             xml_error_string(xml_get_error_code($this->resParser)),
797             xml_get_current_line_number($this->resParser)));
798             }
799                             
800             xml_parser_free($this->resParser);
801             
802             return $this->arrOutput;
803     }
804     function tagOpen($parser, $name, $attrs) {
805         $tag=array("name"=>$name,"attrs"=>$attrs);
806         array_push($this->arrOutput,$tag);
807     }
808     
809     function tagData($parser, $tagData) {
810         if(trim($tagData)) {
811             if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) {
812                 $this->arrOutput[count($this->arrOutput)-1]['tagData'] .= "\n".$tagData;
813             }
814             else {
815                 $this->arrOutput[count($this->arrOutput)-1]['tagData'] = $tagData;
816             }
817         }
818     }
819     
820     function tagClosed($parser, $name) {
821         $this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this->arrOutput)-1];
822         array_pop($this->arrOutput);
823     }
824     
825     function recursive_parseLevel($items) {
826         $array = array();
827         foreach (array_keys($items) as $idx) {
828             $items[$idx]['name'] = strtolower($items[$idx]['name']);
829             
830             $multi = false;
831             if (isset($array[ $items[$idx]['name'] ])) {
832                 // this child is already set, so we're adding multiple items to an array
833                 
834                 if (!is_array($array[ $items[$idx]['name'] ]) || !isset($array[ $items[$idx]['name'] ][0])) {
835                     // hasn't already been made into a numerically-indexed array, so do that now
836                     // we're basically moving the current contents of this item into a 1-item array (at the
837                     // original location) so that we can add a second item in the code below
838                     $array[ $items[$idx]['name'] ] = array( $array[ $items[$idx]['name'] ] );
839                 }
840                 $multi = true;
841             }
842             
843             if (isset($items[$idx]['children']) && is_array($items[$idx]['children'])) {
844                 if ($multi) {
845                     $array[ $items[$idx]['name'] ][] = $this->recursive_parseLevel($items[$idx]['children']);
846                 } else {
847                     $array[ $items[$idx]['name'] ] = $this->recursive_parseLevel($items[$idx]['children']);
848                 }
849             } else if (isset($items[$idx]['tagData'])) {
850                 if ($multi) {
851                     $array[ $items[$idx]['name'] ][] = $items[$idx]['tagData'];
852                 } else {
853                     $array[ $items[$idx]['name'] ] = $items[$idx]['tagData'];
854                 }
855             }
856         }
857         return $array;
858     }
859     
860     function parseAdvanced($strInputXML) {
861         $array = $this->parse($strInputXML);
862         return $this->recursive_parseLevel($array);
863     }
864 }
865
866 /*
867     Return a much more manageable assoc array with module data.
868 */
869 class xml2ModuleArray extends xml2Array {
870     function parseModulesXML($strInputXML) {
871         $array = $this->parseAdvanced($strInputXML);
872         if (isset($array['xml'])) {
873             foreach ($array['xml'] as $key=>$module) {
874                 if ($key == 'module') {
875                     // copy the structure verbatim
876                     $modules[ $module['name'] ] = $module;
877                     // add in a couple that aren't normally there..
878                     $modules[ $module['name'] ] = $module;
879                 }
880             }
881         }
882         
883         // if you are confused about what's happening below, uncomment this why we do it
884         // echo "<pre>"; print_r($arrOutput); echo "</pre>";
885         
886         // ignore the regular xml garbage ([0]['children']) & loop through each module
887         if(!is_array($arrOutput[0]['children'])) return false;
888         foreach($arrOutput[0]['children'] as $module) {
889             if(!is_array($module['children'])) return false;
890             // loop through each modules's tags
891             foreach($module['children'] as $modTags) {
892                     if(isset($modTags['children']) && is_array($modTags['children'])) {
893                         $$modTags['name'] = $modTags['children'];
894                         // loop if there are children (menuitems and requirements)
895                         foreach($modTags['children'] as $subTag) {
896                             $subTags[strtolower($subTag['name'])] = $subTag['tagData'];
897                         }
898                         $$modTags['name'] = $subTags;
899                         unset($subTags);
900                     } else {
901                         // create a variable for each tag we find
902                         $$modTags['name'] = $modTags['tagData'];
903                     }
904
905             }
906             // now build our return array
907             $arrModules[$RAWNAME]['rawname'] = $RAWNAME;    // This has to be set
908             $arrModules[$RAWNAME]['displayName'] = $NAME;    // This has to be set
909             $arrModules[$RAWNAME]['version'] = $VERSION;     // This has to be set
910             $arrModules[$RAWNAME]['type'] = isset($TYPE)?$TYPE:'setup';
911             $arrModules[$RAWNAME]['category'] = isset($CATEGORY)?$CATEGORY:'Unknown';
912             $arrModules[$RAWNAME]['info'] = isset($INFO)?$INFO:'http://www.freepbx.org/wiki/'.$RAWNAME;
913             $arrModules[$RAWNAME]['location'] = isset($LOCATION)?$LOCATION:'local';
914             $arrModules[$RAWNAME]['items'] = isset($MENUITEMS)?$MENUITEMS:null;
915             $arrModules[$RAWNAME]['requirements'] = isset($REQUIREMENTS)?$REQUIREMENTS:null;
916             $arrModules[$RAWNAME]['md5sum'] = isset($MD5SUM)?$MD5SUM:null;
917             //print_r($arrModules);
918             //unset our variables
919             unset($NAME);
920             unset($VERSION);
921             unset($TYPE);
922             unset($CATEGORY);
923             unset($AUTHOR);
924             unset($EMAIL);
925             unset($LOCATION);
926             unset($MENUITEMS);
927             unset($REQUIREMENTS);
928             unset($MD5SUM);
929         }
930         //echo "<pre>"; print_r($arrModules); echo "</pre>";
931
932         return $arrModules;
933     }
934 }
935
936
937
938 class moduleHook {
939     var $hookHtml = '';
940     var $arrHooks = array();
941     
942     function install_hooks($viewing_itemid,$target_module,$target_menuid = '') {
943         global $active_modules;
944         // loop through all active modules
945         foreach($active_modules as $this_module) {
946                 // look for requested hooks for $module
947                 // ie: findme_hook_extensions()
948                 $funct = $this_module['rawname'] . '_hook_' . $target_module;
949                 if( function_exists( $funct ) ) {
950                     // execute the function, appending the
951                     // html output to that of other hooking modules
952                     if ($hookReturn = $funct($viewing_itemid,$target_menuid))
953                         $this->hookHtml .= $hookReturn;
954                     // remember who installed hooks
955                     // we need to know this for processing form vars
956                     $this->arrHooks[] = $this_module['rawname'];
957                 }
958         }
959     }
960     
961     // process the request from the module we hooked
962     function process_hooks($viewing_itemid, $target_module, $target_menuid, $request) {
963         if(is_array($this->arrHooks)) {
964             foreach($this->arrHooks as $hookingMod) {
965                 // check if there is a processing function
966                 $funct = $hookingMod . '_hookProcess_' . $target_module;
967                 if( function_exists( $funct ) ) {
968                     $funct($viewing_itemid, $request);
969                 }
970             }
971         }
972     }
973 }
974
975 function execSQL( $file )
976 {
977     global $db;
978     $data = null;
979     
980     // run sql script
981     $fd = fopen( $file ,"r" );
982     
983     while (!feof($fd)) {
984         $data .= fread($fd, 1024);
985     }
986     fclose($fd);
987     
988     preg_match_all("/((SELECT|INSERT|UPDATE|DELETE|CREATE|DROP).*);\s*\n/Us", $data, $matches);
989     foreach ($matches[1] as $sql) {
990         $result = $db->query($sql);
991         if(DB::IsError($result)) { return false; }
992     }
993 }
994
995 // Dragged this in from page.modules.php, so it can be used by install_amp.
996 function runModuleSQL($moddir,$type){
997     global $amp_conf;
998     $db_engine = $amp_conf["AMPDBENGINE"];
999
1000     $data='';
1001     
1002     // if there is an sql file, run it
1003     // don't forget about our 2 sql syntaxes
1004     if (($db_engine  == "mysql") || ($db_engine == "pgsql")) {
1005         if (is_file($amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.sql")) {
1006             execSQL( $amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.sql" );
1007         }
1008     }
1009     elseif ($db_engine  == "sqlite"){
1010         if (is_file($amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.sqlite")) {
1011             execSQL( $amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.sqlite" );
1012         }
1013     }
1014     else{
1015         // what to do here?
1016         // in general this should fail in earliers stages...
1017     }
1018     
1019     // if there is a php file, run it
1020     if (is_file($amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.php")) {
1021         include($amp_conf["AMPWEBROOT"]."/admin/modules/{$moddir}/{$type}.php");
1022     }
1023     return true;
1024 }
1025
1026 /*
1027 // just for testing hooks, i'll delete it later
1028 function queues_hook_core($viewing_itemid, $target_menuid) {
1029     switch ($target_menuid) {
1030         case 'did':
1031             //get the current setting for this display (if any)
1032             $alertinfo = $viewing_itemid;
1033             return '
1034                 <tr>
1035                     <td><a href="#" class="info">'._("Alert Info").'<span>'._('ALERT_INFO can be used for distinctive ring with SIP devices.').'</span></a>:</td>
1036                     <td><input type="text" name="alertinfo" size="10" value="'.(($alertinfo) ? $alertinfo : "") .'"></td>
1037                 </tr>
1038             ';
1039         break;
1040         default:
1041             return false;
1042         break;
1043     }
1044 }
1045
1046 function queues_hookProcess_core($viewing_itemid, $request) {
1047     switch ($request['action']) {
1048         case 'edtIncoming':
1049             echo "<h1>HI</h1>";
1050             return '
1051                 <tr>
1052                     <td><a href="#" class="info">'._("Alert Info").'<span>'._('ALERT_INFO can be used for distinctive ring with SIP devices.').'</span></a>:</td>
1053                     <td><input type="text" name="alertinfo" size="10" value="'.(($alertinfo) ? $alertinfo : "") .'"></td>
1054                 </tr>
1055             ';
1056         break;
1057         default:
1058             return false;
1059         break;
1060     }
1061 }
1062 */
1063
1064 /** Module functions
1065  */
1066  
1067 /** Get the latest module.xml file for this freePBX version.
1068  * Caches in the database for 5 mintues.
1069  * If $module is specified, only returns the data for that module
1070  */
1071 function module_getonlinexml($module = false) { // was getModuleXml()
1072     global $amp_conf;
1073     //this should be in an upgrade file ... putting here for now.
1074     sql('CREATE TABLE IF NOT EXISTS module_xml (time INT NOT NULL , data BLOB NOT NULL) TYPE = MYISAM ;');
1075     
1076     $result = sql('SELECT * FROM module_xml','getRow',DB_FETCHMODE_ASSOC);
1077     // if the epoch in the db is more than 2 hours old, or the xml is less than 100 bytes, then regrab xml
1078     // Changed to 5 minutes while not in release. Change back for released version.
1079     //
1080     // used for debug, time set to 0 to always fall through
1081     // if((time() - $result['time']) > 0 || strlen($result['data']) < 100 ) {
1082     if((time() - $result['time']) > 300 || strlen($result['data']) < 100 ) {
1083         $version = getversion();
1084         $version = $version[0][0];
1085         // we need to know the freepbx major version we have running (ie: 2.1.2 is 2.1)
1086         preg_match('/(\d+\.\d+)/',$version,$matches);
1087         //echo "the result is ".$matches[1];
1088         if (isset($amp_conf["AMPMODULEXML"])) {
1089             $fn = $amp_conf["AMPMODULEXML"]."modules-".$matches[1].".xml";
1090             // echo "(From amportal.conf)"; //debug
1091         } else {
1092         $fn = "http://mirror.freepbx.org/modules-".$matches[1].".xml";
1093             // echo "(From default)"; //debug
1094         }
1095         //$fn = "/usr/src/freepbx-modules/modules.xml";
1096         $data = file_get_contents($fn);
1097         // remove the old xml
1098         sql('DELETE FROM module_xml');
1099         // update the db with the new xml
1100         $data4sql = (get_magic_quotes_gpc() ? $data : addslashes($data));
1101         sql('INSERT INTO module_xml (time,data) VALUES ('.time().',"'.$data4sql.'")');
1102     } else {
1103 //        echo "using cache";
1104         $data = $result['data'];
1105     }
1106     //echo time() - $result['time'];
1107     $parser = new xml2ModuleArray($data);
1108     $xmlarray = $parser->parseAdvanced($data);
1109     //$modules = $xmlarray['XML']['MODULE'];
1110     
1111     //echo "<hr>Raw XML Data<pre>"; print_r(htmlentities($data)); echo "</pre>";
1112     //echo "<hr>XML2ARRAY<pre>"; print_r($xmlarray); echo "</pre>";
1113     
1114     
1115     if (isset($xmlarray['xml']['module'])) {
1116     
1117         if ($module != false) {
1118             foreach ($xmlarray['xml']['module'] as $mod) {
1119                 if ($module == $mod['rawname']) {
1120                     return $module;
1121                 }
1122             }
1123             return null;
1124         } else {
1125         
1126         
1127             $modules = array();
1128             foreach ($xmlarray['xml']['module'] as $mod) {
1129                 $modules[ $mod['rawname'] ] = $mod;
1130             }
1131             return $modules;
1132         }
1133     }
1134     return null;
1135 }
1136
1137 /** Looks through the modules directory and modules database and returns all available
1138  * information about one or all modules
1139  * @param string  (optional) The module name to query, or false for all module
1140  * @param mixed   (optional) The status(es) to show, using MODULE_STATUS_* constants. Can
1141  *                either be one value, or an array of values.
1142  */
1143 function module_getinfo($module = false, $status = false) {
1144     global $amp_conf, $db;
1145     $modules = array();
1146     
1147     if ($module) {
1148         // get info on only one module
1149         $modules[$module] = _module_readxml($module);
1150         $sql = 'SELECT * FROM modules WHERE modulename = "'.$module.'"';
1151     } else {
1152         // get info on all modules
1153         $dir = opendir($amp_conf['AMPWEBROOT'].'/admin/modules');
1154         while ($file = readdir($dir)) {
1155             if (($file != ".") && ($file != "..") && ($file != "CVS") &&
1156                 ($file != ".svn") && ($file != "_cache") &&
1157                 is_dir($amp_conf['AMPWEBROOT'].'/admin/modules/'.$file)) {
1158                 
1159                 $modules[$file] = _module_readxml($file);
1160                 // if status is anything else, it will be updated below when we read the db
1161                 $modules[$file]['status'] = MODULE_STATUS_NOTINSTALLED;
1162             }
1163         }
1164         $sql = 'SELECT * FROM modules';
1165     }
1166     
1167     // determine details about this module from database
1168     // modulename should match the directory name
1169     
1170     $results = $db->getAll($sql,DB_FETCHMODE_ASSOC);
1171     if(DB::IsError($results)) {
1172         die($results->getMessage());
1173     }
1174     
1175     if (is_array($results)) {
1176         foreach($results as $row) {
1177             if (isset($modules[ $row['modulename'] ])) {
1178                 if ($row['enabled'] != 0) {
1179                     
1180                     // check if file and registered versions are the same
1181                     // version_compare returns 0 if no difference
1182                     if (version_compare($row['version'], $modules[ $row['modulename'] ]['version']) == 0) {
1183                         $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_ENABLED;
1184                     } else {
1185                         $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_NEEDUPGRADE;
1186                     }
1187                     
1188                 } else {
1189                     $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_DISABLED;
1190                 }
1191             } else {
1192                 // no directory for this db entry
1193                 $modules[ $row['modulename'] ]['status'] = MODULE_STATUS_BROKEN;
1194             }
1195             $modules[ $row['modulename'] ]['dbversion'] = $row['version'];
1196         }
1197     }
1198     
1199     if ($status !== false) {
1200         if (!is_array($status)) {
1201             // make a one element array so we can use in_array below
1202             $status = array($status);
1203         }
1204         
1205         foreach (array_keys($modules) as $name) {
1206             if (!in_array($modules[$name]['status'], $status)) {
1207                 // not found in the $status array, remove it
1208                 unset($modules[$name]);
1209             }
1210         }
1211     }
1212     
1213     return $modules;
1214 }
1215
1216 /** Check if a module meets dependencies.
1217  * @param string  The array from a parsed module.xml for this module.
1218  * @return mixed  Returns true if dependencies are met, or an array
1219  *                containing a list of human-readable errors if not.
1220  *                NOTE: you must use strict type checking (===) to test
1221  *                for true, because  array() == true !
1222  */
1223 function module_checkdepends($modulexml) {
1224     $errors = array();
1225     
1226     if (isset($modulexml['depends'])) {
1227         foreach ($modulexml['depends'] as $type => $requirements) {
1228             // if only a single item, make it an array so we can use the same code as for multiple items
1229             if (!is_array($requirements)) {
1230                 $requirements = array($requirements);
1231             }
1232             
1233             foreach ($requirements as $value) {
1234                 switch ($type) {
1235                     case 'version':
1236                         if (preg_match('/^([a-zA-Z_]+)(\s+(>=|>|=|<|<=|!=)?(\d(\.\d)*))?$/i', $value, $matches)) {
1237                             // matches[1] = operator, [2] = version
1238                         }
1239                     break;
1240                     case 'module':
1241                         if (preg_match('/^([a-z_]+)(\s+(>=|>|=|<|<=|!=)?(\d(\.\d)*))?$/i', $value, $matches)) {
1242                             // matches[1] = modulename, [3]=comparison operator, [4] = version
1243                         }
1244                     break;
1245                     case 'file': // file exists
1246                         if (!file_exists($value)) {
1247                             $errors[] = 'File '.$value.' must exist.';
1248                         }
1249                     break;
1250                     case 'engine':
1251                         if (preg_match('/^([a-z_]+)(\s+(>=|>|=|<|<=|!=)?(\d(\.\d)*))?$/i', $value, $matches)) {
1252                             // matches[1] = engine, [3]=comparison operator, [4] = version
1253                         }
1254                     break;
1255                 }
1256             }
1257         }
1258         
1259     }
1260 }
1261
1262 /** Downloads the latest version of a module
1263  * and extracts it to the directory
1264  */
1265 function module_download($modulename) { // was fetchModule
1266     function untar_module($filename, $target) {
1267         global $amp_conf;
1268         system("tar zxf ".escapeshellarg($filename)." --directory=".escapeshellarg($target));
1269         return true;
1270     }
1271     
1272     global $amp_conf;
1273     $res = module_getonlinexml($modulename);
1274     if ($res == null) {
1275         echo "<div class=\"error\">"._("Unaware of module")." {$name}</div>";
1276         return false;
1277     }
1278     
1279     $file = basename($res['location']);
1280     $filename = $amp_conf['AMPWEBROOT']."/admin/modules/_cache/".$file;
1281     if (file_exists($filename)) {
1282         // We might already have it! Let's check the MD5.
1283         $filedata = "";
1284         if ( $fh = @ fopen($filename, "r") ) {
1285             while (!feof($fh)) {
1286                 $filedata .= fread($fh, 8192);
1287             }
1288             fclose($fh);
1289         }
1290         
1291         if (isset($res['md5sum']) && $res['md5sum'] == md5 ($filedata)) {
1292             // Note, if there's no MD5 information, it will redownload
1293             // every time. Otherwise theres no way to avoid a corrupt
1294             // download
1295             
1296             return untar_module($filename, $amp_conf['AMPWEBROOT'].'/admin/modules/');
1297         } else {
1298             unlink($filename);
1299         }
1300     }
1301     
1302     if (isset($amp_conf['AMPMODULESVN'])) {
1303         $url = $amp_conf['AMPMODULESVN'].$res['location'];
1304         // echo "(From amportal.conf)"; // debug
1305     } else {
1306         $url = "http://mirror.freepbx.org/modules/".$res['location'];
1307         // echo "(From default)"; // debug
1308     }
1309     
1310     if ($fp = @fopen($filename,"w")) {
1311         $filedata = file_get_contents($url);
1312         fwrite($fp,$filedata);
1313         fclose($fp);
1314     }
1315     
1316     if (is_readable($filename) !== TRUE ) {
1317         echo "<div class=\"error\">"._("Unable to save")." {$filename} - Check file/directory permissions</div>";
1318         return false;
1319     }
1320     
1321     // Check the MD5 info against what's in the module's XML
1322     if (!isset($res['md5sum']) || empty($res['md5sum'])) {
1323         echo "<div class=\"error\">"._("Unable to Locate Integrity information for")." {$filename} - "._("Continuing Anyway")."</div>";
1324     } elseif ($res['md5sum'] != md5 ($filedata)) {
1325         echo "<div class=\"error\">"._("File Integrity FAILED for")." {$filename} - "._("Aborting")."</div>";
1326         unlink($filename);
1327         return false;
1328     }
1329     
1330     return untar_module($filename, $amp_conf['AMPWEBROOT'].'/admin/modules/');
1331     
1332 }
1333
1334 function _module_readxml($modulename) {
1335     global $amp_conf;
1336     $dir = $amp_conf['AMPWEBROOT'].'/admin/modules/'.$modulename;
1337     if (is_dir($dir) && file_exists($dir.'/module.xml')) {
1338         $data = file_get_contents($dir.'/module.xml');
1339         //$parser = new xml2ModuleArray($data);
1340         //$xmlarray = $parser->parseModulesXML($data);
1341         $parser = new xml2Array($data);
1342         $xmlarray = $parser->parseAdvanced($data);
1343         if (isset($xmlarray['module'])) {
1344             // add a couple fields first
1345             $xmlarray['module']['displayname'] = $xmlarray['module']['name'];
1346             if (isset($xmlarray['module']['menuitems'])) {
1347                 $xmlarray['module']['items'] = $xmlarray['module']['menuitems'];
1348             }
1349             return $xmlarray['module'];
1350         }
1351     }
1352     return null;
1353 }
1354
1355 /** Installs or upgrades a module from it's directory
1356  * Checks dependencies, and enables
1357  */
1358 function module_install($modulename) {
1359     $dir = $amp_conf['AMPWEBROOT'].'/admin/modules/'.$modulename;
1360     if (is_dir($dir) && file_exists($dir.'/module.xml')) {
1361     }
1362     
1363     
1364 }
1365
1366 function module_enable($modulename) { // was enableModule
1367 }
1368
1369 function module_disable($modulename) { // was disableModule
1370     global $db;
1371     $sql = 'UPDATE modules SET enabled = 0 WHERE modulename = "'.$modulename.'"';
1372     $results = $db->query($sql);
1373     if(DB::IsError($results)) {
1374         die($results->getMessage());
1375     }
1376 }
1377
1378 /** Totally deletes a module
1379  */
1380 function module_delete($modulename) {
1381 }
1382
1383 // runModuleSQL moved to functions.inc.php
1384 /*
1385 function installModule($modname,$modversion)
1386 {
1387     global $db;
1388     global $amp_conf;
1389     
1390     switch ($amp_conf["AMPDBENGINE"])
1391     {
1392         case "sqlite":
1393             // to support sqlite2, we are not using autoincrement. we need to find the
1394             // max ID available, and then insert it
1395             $sql = "SELECT max(id) FROM modules;";
1396             $results = $db->getRow($sql);
1397             $new_id = $results[0];
1398             $new_id ++;
1399             $sql = "INSERT INTO modules (id,modulename, version,enabled) values ('{$new_id}','{$modname}','{$modversion}','0' );";
1400             break;
1401         
1402         default:
1403             $sql = "INSERT INTO modules (modulename, version) values ('{$modname}','{$modversion}');";
1404         break;
1405     }
1406
1407     $results = $db->query($sql);
1408     if(DB::IsError($results)) {
1409         die($results->getMessage());
1410     }
1411 }
1412
1413 function uninstallModule($modname) {
1414     global $db;
1415     $sql = "DELETE FROM modules WHERE modulename = '{$modname}'";
1416     $results = $db->query($sql);
1417     if(DB::IsError($results)) {
1418         die($results->getMessage());
1419     }
1420 }
1421
1422 /** downloads a module, and extracts it into the module dir
1423  * /
1424 function module_fetch($name) { // was fetchModule
1425     global $amp_conf;
1426     $res = module_getonlinexml($modulename);
1427     if (!isset($res)) {
1428         echo "<div class=\"error\">"._("Unaware of module")." {$name}</div>";
1429         return false;
1430     }
1431     $file = basename($res['location']);
1432     $filename = $amp_conf['AMPWEBROOT']."/admin/modules/_cache/".$file;
1433     if(file_exists($filename)) {
1434         // We might already have it! Let's check the MD5.
1435         $filedata = "";
1436         $fh = @fopen($filename, "r");
1437         while (!feof($fh)) {
1438             $filedata .= fread($fh, 8192);
1439         }
1440         if (isset($res['md5sum']) && $res['md5sum'] == md5 ($filedata)) {
1441             // Note, if there's no MD5 information, it will redownload
1442             // every time. Otherwise theres no way to avoid a corrupt
1443             // download
1444             
1445             return verifyAndInstall($filename);
1446         } else {
1447             unlink($filename);
1448         }
1449     }
1450     if (isset($amp_conf['AMPMODULESVN'])) {
1451         $url = $amp_conf['AMPMODULESVN'].$res['location'];
1452         // echo "(From amportal.conf)"; // debug
1453     } else {
1454     $url = "http://mirror.freepbx.org/modules/".$res['location'];
1455         // echo "(From default)"; // debug
1456     }
1457     $fp = @fopen($filename,"w");
1458     $filedata = file_get_contents($url);
1459     fwrite($fp,$filedata);
1460     fclose($fp);
1461     if (is_readable($filename) !== TRUE ) {
1462         echo "<div class=\"error\">"._("Unable to save")." {$filename} - Check file/directory permissions</div>";
1463         return false;
1464     }
1465     // Check the MD5 info against what's in the module's XML
1466     if (!isset($res['md5sum']) || empty($res['md5sum'])) {
1467         echo "<div class=\"error\">"._("Unable to Locate Integrity information for")." {$filename} - "._("Continuing Anyway")."</div>";
1468     } elseif ($res['md5sum'] != md5 ($filedata)) {
1469         echo "<div class=\"error\">"._("File Integrity FAILED for")." {$filename} - "._("Aborting")."</div>";
1470         unlink($filename);
1471         return false;
1472     }
1473     // verifyAndInstall does the untar, and will do the signed-package check.
1474     return verifyAndInstall($filename);
1475
1476 }
1477
1478 function upgradeModule($module, $allmods = NULL) {
1479     if($allmods === NULL)
1480         $allmods = find_allmodules();
1481     // the install.php can set this to false if the upgrade fails.
1482     $success = true;
1483     if(is_file("modules/$module/install.php"))
1484         include "modules/$module/install.php";
1485     if ($success) {
1486         sql('UPDATE modules SET version = "'.$allmods[$module]['version'].'" WHERE modulename = "'.$module.'"');
1487         needreload();
1488     }
1489 }
1490
1491 function rmModule($module) {
1492     global $amp_conf;
1493     if($module != 'core') {
1494         if (is_dir($amp_conf['AMPWEBROOT'].'/admin/modules/'.$module) && strstr($module, '.') === FALSE ) {
1495             exec('/bin/rm -rf '.$amp_conf['AMPWEBROOT'].'/admin/modules/'.$module);
1496         }
1497     } else {
1498         echo "<script language=\"Javascript\">alert('"._("You cannot delete the Core module")."');</script>";
1499     }
1500 }
1501
1502 */
1503
1504
1505 function freepbx_log($section, $level, $message) {
1506         global $db;
1507         global $debug; // This is used by retrieve_conf
1508
1509         $sth = $db->prepare("INSERT INTO freepbx_log (time, section, level, message) VALUES (NOW(),?,?,?)");
1510         $db->execute($sth, array($section, $level, $message));
1511         if (isset($debug) && ($debug != false))
1512                 print "[DEBUG-$section] ($level) $message\n";
1513 }
1514 ?>
1515
Note: See TracBrowser for help on using the browser.