root/modules/branches/2.4/core/functions.inc.php

Revision 10820, 189.4 kB (checked in by p_lindheimer, 1 year ago)

closes #4730 adds useragent with FreePBX and Asterisk Version numbers

  • Property svn:mime-type set to text/plain
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php
2
3 class core_conf {
4   var $_sip_general    = array();
5   var $_iax_general    = array();
6   var $_featuregeneral = array();
7   var $_featuremap     = array();
8   var $_applicationmap = array();
9   // return an array of filenames to write
10   function get_filename() {
11     $files = array(
12       'sip_additional.conf',
13       'sip_registrations.conf',
14       'iax_additional.conf',
15       'iax_registrations.conf',
16       'zapata_additional.conf',
17       'sip_general_additional.conf',
18       'iax_general_additional.conf',
19       'features_general_additional.conf',
20       'features_applicationmap_additional.conf',
21       'features_featuremap_additional.conf',
22       );
23     return $files;
24   }
25  
26   // return the output that goes in each of the files
27   function generateConf($file) {
28     global $version;
29
30     switch ($file) {
31       case 'sip_general_additional.conf':
32         return $this->generate_sip_general_additional($version);
33         break;
34       case 'sip_additional.conf':
35         return $this->generate_sip_additional($version);
36         break;
37       case 'sip_registrations.conf':
38         return $this->generate_sip_registrations($version);
39         break;
40       case 'iax_general_additional.conf':
41         return $this->generate_iax_general_additional($version);
42         break;
43       case 'iax_additional.conf':
44         return $this->generate_iax_additional($version);
45         break;
46       case 'iax_registrations.conf':
47         return $this->generate_iax_registrations($version);
48         break;
49       case 'zapata_additional.conf':
50         return $this->generate_zapata_additional($version);
51         break;
52       case 'features_general_additional.conf':
53         return $this->generate_featuregeneral_additional($version);
54         break;
55       case 'features_applicationmap_additional.conf':
56         return $this->generate_applicationmap_additional($version);
57         break;
58       case 'features_featuremap_additional.conf':
59         return $this->generate_featuremap_additional($version);
60         break;
61     }
62   }
63
64   function addSipGeneral($key, $value) {
65     $this->_sip_general[] = array('key' => $key, 'value' => $value);
66   }
67
68   function generate_sip_general_additional($ast_version) {
69     $output = '';
70
71     if (isset($this->_sip_general) && is_array($this->_sip_general)) {
72       foreach ($this->_sip_general as $values) {
73         $output .= $values['key']."=".$values['value']."\n";
74       }
75     }
76     return $output;
77   }
78
79   function addIaxGeneral($key, $value) {
80     $this->_iax_general[] = array('key' => $key, 'value' => $value);
81   }
82
83   function generate_iax_general_additional($ast_version) {
84     $output = '';
85
86     if (isset($this->_iax_general) && is_array($this->_iax_general)) {
87       foreach ($this->_iax_general as $values) {
88         $output .= $values['key']."=".$values['value']."\n";
89       }
90     }
91     return $output;
92   }
93
94   function addFeatureGeneral($key, $value) {
95     $this->_featuregeneral[] = array('key' => $key, 'value' => $value);
96   }
97
98   function generate_featuregeneral_additional($ast_version) {
99     $output = '';
100
101     if (isset($this->_featuregeneral) && is_array($this->_featuregeneral)) {
102       foreach ($this->_featuregeneral as $values) {
103         $output .= $values['key']."=".$values['value']."\n";
104       }
105     }
106     return $output;
107   }
108
109   function addFeatureMap($key, $value) {
110     $this->_featuremap[] = array('key' => $key, 'value' => $value);
111   }
112
113   function generate_featuremap_additional($ast_version) {
114     $output = '';
115
116     if (isset($this->_featuremap) && is_array($this->_featuremap)) {
117       foreach ($this->_featuremap as $values) {
118         $output .= $values['key']."=".$values['value']."\n";
119       }
120     }
121     return $output;
122   }
123
124   function addApplicationMap($key, $value) {
125     $this->_applicationmap[] = array('key' => $key, 'value' => $value);
126   }
127
128   function generate_applicationmap_additional($ast_version) {
129     $output = '';
130
131     if (isset($this->_applicationmap) && is_array($this->_applicationmap)) {
132       foreach ($this->_applicationmap as $values) {
133         $output .= $values['key']."=".$values['value']."\n";
134       }
135     }
136     return $output;
137   }
138
139   function generate_sip_additional($ast_version) {
140     global $db;
141
142     $table_name = "sip";
143     $additional = "";
144     $output = "";
145
146     // Asterisk 1.4 requires call-limit be set for hints to work properly
147     //
148     if (version_compare($ast_version, "1.4", "ge")) {
149       $call_limit = "call-limit=50\n";
150       $ver12 = false;
151     } else {
152       $call_limit = "";
153       $ver12 = true;
154     }
155
156     $sql = "SELECT keyword,data from $table_name where id=-1 and keyword <> 'account' and flags <> 1";
157     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
158     if(DB::IsError($results)) {
159       die($results->getMessage());
160     }
161     foreach ($results as $result) {
162       if ($ver12) {
163         $additional .= $result['keyword']."=".$result['data']."\n";
164       } else {
165         $option = $result['data'];
166         switch (strtolower($result['keyword'])) {
167           case 'insecure':
168             if ($option == 'very')
169               $additional .= "insecure=port,invite\n";
170             else if ($option == 'yes')
171               $additional .= "insecure=port\n";
172             else
173               $additional .= $result['keyword']."=$option\n";
174             break;
175           case 'allow':
176           case 'disallow':
177             if ($option != '')
178               $additional .= $result['keyword']."=$option\n";
179             break;
180           default:
181             $additional .= $result['keyword']."=$option\n";
182         }
183       }
184     }
185
186     $sql = "SELECT data,id from $table_name where keyword='account' and flags <> 1 group by data";
187     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
188     if(DB::IsError($results)) {
189       die($results->getMessage());
190     }
191
192     foreach ($results as $result) {
193       $account = $result['data'];
194       $id = $result['id'];
195       $output .= "[$account]\n";
196  
197       $sql = "SELECT keyword,data from $table_name where id='$id' and keyword <> 'account' and flags <> 1 order by flags, keyword DESC";
198       $results2 = $db->getAll($sql, DB_FETCHMODE_ASSOC);
199       if(DB::IsError($results2)) {
200         die($results2->getMessage());
201       }
202       foreach ($results2 as $result2) {
203         $options = explode("&", $result2['data']);
204         if ($ver12) {
205           foreach ($options as $option) {
206             $output .= $result2['keyword']."=$option\n";
207           }
208         } else {
209           foreach ($options as $option) {
210             switch (strtolower($result2['keyword'])) {
211               case 'insecure':
212                 if ($option == 'very')
213                   $output .= "insecure=port,invite\n";
214                 else if ($option == 'yes')
215                   $output .= "insecure=port\n";
216                 else
217                   $output .= $result2['keyword']."=$option\n";
218                 break;
219               case 'allow':
220               case 'disallow':
221                 if ($option != '')
222                   $output .= $result2['keyword']."=$option\n";
223                 break;
224               default:
225                 $output .= $result2['keyword']."=$option\n";
226             }
227           }
228         }
229       }
230       if ($call_limit && (substr($id,0,4) != "9999" | $id < 99990)) {
231
232         $output .= $call_limit;
233       }
234       $output .= $additional."\n";
235     }
236     return $output;
237   }
238
239   function generate_sip_registrations($ast_version) {
240     global $db;
241
242     $table_name = "sip";
243     $output = "";
244
245     // items with id like 9999999% get put in registrations file
246     //
247     $sql = "SELECT keyword,data from $table_name where id LIKE '9999999%' and keyword <> 'account' and flags <> 1";
248     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
249     if(DB::IsError($results)) {
250       die($results->getMessage());
251     }
252
253     foreach ($results as $result) {
254       $output .= $result['keyword']."=".$result['data']."\n";
255     }
256
257     return $output;
258   }
259
260   function generate_iax_additional($ast_version) {
261     global $db;
262
263     $table_name = "iax";
264     $additional = "";
265     $output = "";
266
267     $ver12 = version_compare($ast_version, '1.4', 'lt');
268
269     $sql = "SELECT keyword,data from $table_name where id=-1 and keyword <> 'account' and flags <> 1";
270     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
271     if(DB::IsError($results)) {
272       die($results->getMessage());
273     }
274     foreach ($results as $result) {
275       if ($ver12) {
276         $additional .= $result['keyword']."=".$result['data']."\n";
277       } else {
278         $option = $result['data'];
279         switch ($result['keyword']) {
280           case 'notransfer':
281             if (strtolower($option) == 'yes') {
282               $additional .= "transfer=no\n";
283             } else if (strtolower($option) == 'no') {
284               $additional .= "transfer=yes\n";
285             } else if (strtolower($option) == 'mediaonly') {
286               $additional .= "transfer=mediaonly\n";
287             } else {
288               $additional .= $result['keyword']."=$option\n";
289             }
290             break;
291           case 'allow':
292           case 'disallow':
293             if ($option != '')
294               $additional .= $result['keyword']."=$option\n";
295             break;
296           default:
297             $additional .= $result['keyword']."=$option\n";
298         }
299       }
300     }
301
302     $sql = "SELECT data,id from $table_name where keyword='account' and flags <> 1 group by data";
303     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
304     if(DB::IsError($results)) {
305       die($results->getMessage());
306     }
307    
308     foreach ($results as $result) {
309       $account = $result['data'];
310       $id = $result['id'];
311       $output .= "[$account]\n";
312  
313       $sql = "SELECT keyword,data from $table_name where id='$id' and keyword <> 'account' and flags <> 1 order by flags, keyword DESC";
314       $results2 = $db->getAll($sql, DB_FETCHMODE_ASSOC);
315       if(DB::IsError($results2)) {
316         die($results2->getMessage());
317       }
318       foreach ($results2 as $result2) {
319         $options = explode("&", $result2['data']);
320         if ($ver12) {
321           foreach ($options as $option) {
322             $output .= $result2['keyword']."=$option\n";
323           }
324         } else {
325           foreach ($options as $option) {
326             switch ($result2['keyword']) {
327               case 'notransfer':
328                 if (strtolower($option) == 'yes') {
329                   $output .= "transfer=no\n";
330                 } else if (strtolower($option) == 'no') {
331                   $output .= "transfer=yes\n";
332                 } else if (strtolower($option) == 'mediaonly') {
333                   $output .= "transfer=mediaonly\n";
334                 } else {
335                   $output .= $result2['keyword']."=$option\n";
336                 }
337                 break;
338               case 'allow':
339               case 'disallow':
340                 if ($option != '')
341                   $output .= $result2['keyword']."=$option\n";
342                 break;
343               default:
344                 $output .= $result2['keyword']."=$option\n";
345             }
346           }
347         }
348       }
349       $output .= $additional."\n";
350     }
351     return $output;
352   }
353
354   function generate_iax_registrations($ast_version) {
355     global $db;
356
357     $table_name = "iax";
358     $output = "";
359
360     // items with id like 9999999% get put in the registration file
361     //
362     $sql = "SELECT keyword,data from $table_name where id LIKE '9999999%' and keyword <> 'account' and flags <> 1";
363     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
364     if(DB::IsError($results)) {
365       die($results->getMessage());
366     }
367
368     foreach ($results as $result) {
369       $output .= $result['keyword']."=".$result['data']."\n";
370     }
371
372     return $output;
373   }
374
375   function generate_zapata_additional($ast_version) {
376     global $db;
377
378     $table_name = "zap";
379
380     $additional = "";
381     $output = '';
382
383     $sql = "SELECT keyword,data from $table_name where id=-1 and keyword <> 'account' and flags <> 1";
384     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
385     if(DB::IsError($results)) {
386       die($results->getMessage());
387     }
388     foreach ($results as $result) {
389       $additional .= $result['keyword']."=".$result['data']."\n";
390     }
391
392     $sql = "SELECT data,id from $table_name where keyword='account' and flags <> 1 group by data";
393     $results = $db->getAll($sql, DB_FETCHMODE_ASSOC);
394     if(DB::IsError($results)) {
395       die($results->getMessage());
396     }
397
398     foreach ($results as $result) {
399       $account = $result['data'];
400       $id = $result['id'];
401       $output .= ";;;;;;[$account]\n";
402  
403       $sql = "SELECT keyword,data from $table_name where id=$id and keyword <> 'account' and flags <> 1 order by keyword DESC";
404       $results2 = $db->getAll($sql, DB_FETCHMODE_ASSOC);
405       if(DB::IsError($results2)) {
406         die($results2->getMessage());
407       }
408       $zapchannel="";
409       foreach ($results2 as $result2) {
410         if ($result2['keyword'] == 'channel') {
411           $zapchannel = $result2['data'];
412         } else {
413           $output .= $result2['keyword']."=".$result2['data']."\n";
414         }
415       }
416       $output .= "channel=>$zapchannel\n";
417       $output .= $additional."\n";
418     }
419     return $output;
420   }
421 }
422
423 // The destinations this module provides
424 // returns a associative arrays with keys 'destination' and 'description'
425 function core_destinations() {
426   //static destinations
427   $extens = array();
428   $category = 'Terminate Call';
429   $extens[] = array('destination' => 'app-blackhole,hangup,1', 'description' => 'Hangup', 'category' => $category);
430   $extens[] = array('destination' => 'app-blackhole,congestion,1', 'description' => 'Congestion', 'category' => $category);
431   $extens[] = array('destination' => 'app-blackhole,busy,1', 'description' => 'Busy', 'category' => $category);
432   $extens[] = array('destination' => 'app-blackhole,zapateller,1', 'description' => 'Play SIT Tone (Zapateller)', 'category' => $category);
433   $extens[] = array('destination' => 'app-blackhole,musiconhold,1', 'description' => 'Put caller on hold forever', 'category' => $category);
434   $extens[] = array('destination' => 'app-blackhole,ring,1', 'description' => 'Play ringtones to caller until they hangup', 'category' => $category);
435  
436   //get the list of meetmes
437   $results = core_users_list();
438  
439   if (isset($results) && function_exists('voicemail_getVoicemail')) {
440     //get voicemail
441     $uservm = voicemail_getVoicemail();
442     $vmcontexts = array_keys($uservm);
443     foreach ($results as $thisext) {
444       $extnum = $thisext[0];
445       // search vm contexts for this extensions mailbox
446       foreach ($vmcontexts as $vmcontext) {
447         if(isset($uservm[$vmcontext][$extnum])){
448           //$vmname = $uservm[$vmcontext][$extnum]['name'];
449           //$vmboxes[$extnum] = array($extnum, '"' . $vmname . '" <' . $extnum . '>');
450           $vmboxes[$extnum] = true;
451         }
452       }
453     }
454   }
455  
456   // return an associative array with destination and description
457   // core provides both users and voicemail boxes as destinations
458   if (isset($results)) {
459     foreach($results as $result) {
460       $extens[] = array('destination' => 'from-did-direct,'.$result['0'].',1', 'description' => ' <'.$result['0'].'> '.$result['1'], 'category' => 'Extensions');
461       if(isset($vmboxes[$result['0']])) {
462         $extens[] = array('destination' => 'ext-local,vmb'.$result['0'].',1', 'description' => '<'.$result[0].'> '.$result[1].' (busy)', 'category' => 'Voicemail');
463         $extens[] = array('destination' => 'ext-local,vmu'.$result['0'].',1', 'description' => '<'.$result[0].'> '.$result[1].' (unavail)', 'category' => 'Voicemail');
464         $extens[] = array('destination' => 'ext-local,vms'.$result['0'].',1', 'description' => '<'.$result[0].'> '.$result[1].' (no-msg)', 'category' => 'Voicemail');
465       }
466     }
467   }
468  
469   if (isset($extens))
470     return $extens;
471   else
472     return null;
473 }
474
475 function core_getdest($exten) {
476   $dests[] = 'from-did-direct,'.$exten.',1';
477   if (!function_exists('voicemail_mailbox_get')) {
478     return $dests;
479   }
480   $box = voicemail_mailbox_get($exten);
481   if ($box == null) {
482     return $dests;
483   }
484   $dests[] = 'ext-local,vmb'.$exten.',1';
485   $dests[] = 'ext-local,vmu'.$exten.',1';
486   $dests[] = 'ext-local,vms'.$exten.',1';
487
488   return $dests;
489 }
490
491 function core_getdestinfo($dest) {
492   global $active_modules;
493
494   // Check for Extension Number Destinations
495   //
496   if (substr(trim($dest),0,16) == 'from-did-direct,') {
497     $exten = explode(',',$dest);
498     $exten = $exten[1];
499     $thisexten = core_users_get($exten);
500     if (empty($thisexten)) {
501       return array();
502     } else {
503       //$type = isset($active_modules['announcement']['type'])?$active_modules['announcement']['type']:'setup';
504       $display = ($amp_conf['AMPEXTENSIONS'] == "deviceanduser")?'users':'extensions';
505       return array('description' => 'User Extension '.$exten.': '.$thisexten['name'],
506                    'edit_url' => "config.php?type=setup&display=$display&extdisplay=".urlencode($exten)."&skip=0",
507                   );
508     }
509
510   // Check for voicemail box destinations
511   //
512   } else if (substr(trim($dest),0,12) == 'ext-local,vm') {
513     $exten = explode(',',$dest);
514     $exten = substr($exten[1],3);
515     if (!function_exists('voicemail_mailbox_get')) {
516       return array();
517     }
518     $thisexten = core_users_get($exten);
519     if (empty($thisexten)) {
520       return array();
521     }
522     $box = voicemail_mailbox_get($exten);
523     if ($box == null) {
524       return array();
525     }
526     $display = ($amp_conf['AMPEXTENSIONS'] == "deviceanduser")?'users':'extensions';
527     return array('description' => 'User Extension '.$exten.': '.$thisexten['name'],
528                  'edit_url' => "config.php?type=setup&display=$display&extdisplay=".urlencode($exten)."&skip=0",
529                 );
530
531   // Check for blackhole Termination Destinations
532   //
533   } else if (substr(trim($dest),0,14) == 'app-blackhole,') {
534     $exten = explode(',',$dest);
535     $exten = $exten[1];
536
537     switch ($exten) {
538       case 'hangup':
539         $description = 'Hangup';
540         break;
541       case 'congestion':
542         $description = 'Congestion';
543         break;
544       case 'busy':
545         $description = 'Busy';
546         break;
547       case 'zapateller':
548         $description = 'Play SIT Tone (Zapateller)';
549         break;
550       case 'musiconhold':
551         $description = 'Put caller on hold forever';
552         break;
553       case 'ring':
554         $description = 'Play ringtones to caller';
555         break;
556       default:
557         $description = false;
558     }
559     if ($description) {
560       return array('description' => 'Core: '.$description,
561                    'edit_url' => false,
562                    );
563     } else {
564       return array();
565     }
566
567   // None of the above, so not one of ours
568   //
569   } else {
570     return false;
571   }
572 }
573 /*  Generates dialplan for "core" components (extensions & inbound routing)
574   We call this with retrieve_conf
575 */
576 function core_get_config($engine) {
577   global $ext;  // is this the best way to pass this?
578   global $version;  // this is not the best way to pass this, this should be passetd together with $engine
579   global $amp_conf;
580   global $core_conf;
581
582   $modulename = "core";
583  
584   switch($engine) {
585     case "asterisk":
586
587       // Now add to sip_general_addtional.conf
588       //
589       if (isset($core_conf) && is_a($core_conf, "core_conf")) {
590         $core_conf->addSipGeneral('disallow','all');
591         $core_conf->addSipGeneral('allow','ulaw');
592         $core_conf->addSipGeneral('allow','alaw');
593         $core_conf->addSipGeneral('context','from-sip-external');
594         $core_conf->addSipGeneral('callerid','Unknown');
595         $core_conf->addSipGeneral('notifyringing','yes');
596         if (version_compare($version, '1.4', 'ge')) {
597           $core_conf->addSipGeneral('notifyhold','yes');
598           $core_conf->addSipGeneral('limitonpeers','yes');
599           $core_conf->addSipGeneral('tos_sip','cs3');    // Recommended setting from doc/ip-tos.txt
600           $core_conf->addSipGeneral('tos_audio','ef');   // Recommended setting from doc/ip-tos.txt
601           $core_conf->addSipGeneral('tos_video','af41'); // Recommended setting from doc/ip-tos.txt
602         } else {
603           $core_conf->addSipGeneral('tos','0x68'); // This really doesn't do anything with astersk not running as root
604         }
605         $useragent = "FPBX-".getversion()."($version)";
606         $core_conf->addSipGeneral('useragent',$useragent);
607         $core_conf->addIaxGeneral('disallow','all');
608         $core_conf->addIaxGeneral('allow','ulaw');
609         $core_conf->addIaxGeneral('allow','alaw');
610         $core_conf->addIaxGeneral('allow','gsm');
611         $core_conf->addIaxGeneral('mailboxdetail','yes');
612         if (version_compare($version, '1.4', 'ge')) {
613           $core_conf->addIaxGeneral('tos','ef'); // Recommended setting from doc/ip-tos.txt
614         }
615
616         $fcc = new featurecode($modulename, 'blindxfer');
617         $code = $fcc->getCodeActive();
618         unset($fcc);
619         if ($code != '') {
620           $core_conf->addFeatureMap('blindxfer',$code);
621         }
622
623         $fcc = new featurecode($modulename, 'atxfer');
624         $code = $fcc->getCodeActive();
625         unset($fcc);
626         if ($code != '') {
627           $core_conf->addFeatureMap('atxfer',$code);
628         }
629
630         $fcc = new featurecode($modulename, 'automon');
631         $code = $fcc->getCodeActive();
632         unset($fcc);
633         if ($code != '') {
634           $core_conf->addFeatureMap('automon',$code);
635         }
636
637         $core_conf->addFeatureMap('disconnect','**');
638       }
639
640       // FeatureCodes
641       $fcc = new featurecode($modulename, 'userlogon');
642       $fc_userlogon = $fcc->getCodeActive();
643       unset($fcc);
644
645       $fcc = new featurecode($modulename, 'userlogoff');
646       $fc_userlogoff = $fcc->getCodeActive();
647       unset($fcc);
648
649       $fcc = new featurecode($modulename, 'zapbarge');
650       $fc_zapbarge = $fcc->getCodeActive();
651       unset($fcc);
652
653       $fcc = new featurecode($modulename, 'chanspy');
654       $fc_chanspy = $fcc->getCodeActive();
655       unset($fcc);
656
657       $fcc = new featurecode($modulename, 'simu_pstn');
658       $fc_simu_pstn = $fcc->getCodeActive();
659       unset($fcc);
660
661       $fcc = new featurecode($modulename, 'simu_fax');
662       $fc_simu_fax = $fcc->getCodeActive();
663       unset($fcc);
664
665       $fcc = new featurecode($modulename, 'pickup');
666       $fc_pickup = $fcc->getCodeActive();
667       unset($fcc);
668
669       // Log on / off -- all in one context
670       if ($fc_userlogoff != '' || $fc_userlogon != '') {
671         $ext->addInclude('from-internal-additional', 'app-userlogonoff'); // Add the include from from-internal
672        
673         if ($fc_userlogoff != '') {
674           $ext->add('app-userlogonoff', $fc_userlogoff, '', new ext_macro('user-logoff'));
675           $ext->add('app-userlogonoff', $fc_userlogoff, '', new ext_hangup(''));
676         }
677  
678         if ($fc_userlogon != '') {
679           $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_macro('user-logon'));
680           $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_hangup(''));
681          
682           $clen = strlen($fc_userlogon);
683           $fc_userlogon = "_$fc_userlogon.";
684           $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_macro('user-logon,${EXTEN:'.$clen.'}'));
685           $ext->add('app-userlogonoff', $fc_userlogon, '', new ext_hangup(''));
686         }
687       }
688
689       // Call pickup using app_pickup - Note that '**xtn' is hard-coded into the GXPs and SNOMs as a number to dial
690       // when a user pushes a flashing BLF.
691       if ($fc_pickup != '') {
692         $ext->addInclude('from-internal-additional', 'app-pickup');
693         $fclen = strlen($fc_pickup);
694         $ext->add('app-pickup', "_$fc_pickup.", '', new ext_NoOp('Attempt to Pickup ${EXTEN:'.$fclen.'} by ${CALLERID(num)}'));
695         if (strstr($version, 'BRI'))
696           $ext->add('app-pickup', "_$fc_pickup.", '', new ext_dpickup('${EXTEN:'.$fclen.'}'));
697         else
698           $ext->add('app-pickup', "_$fc_pickup.", '', new ext_pickup('${EXTEN:'.$fclen.'}'));
699       }
700      
701      
702       // zap barge
703       if ($fc_zapbarge != '') {
704         $ext->addInclude('from-internal-additional', 'app-zapbarge'); // Add the include from from-internal
705        
706         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_macro('user-callerid'));
707         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_setvar('GROUP()','${CALLERID(number)}'));
708         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_answer(''));
709         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_wait(1));
710         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_zapbarge(''));
711         $ext->add('app-zapbarge', $fc_zapbarge, '', new ext_hangup(''));
712       }
713
714       // chan spy
715       if ($fc_chanspy != '') {
716         $ext->addInclude('from-internal-additional', 'app-chanspy'); // Add the include from from-internal
717         $ext->add('app-chanspy', $fc_chanspy, '', new ext_macro('user-callerid'));
718         $ext->add('app-chanspy', $fc_chanspy, '', new ext_answer(''));
719         $ext->add('app-chanspy', $fc_chanspy, '', new ext_wait(1));
720         $ext->add('app-chanspy', $fc_chanspy, '', new ext_chanspy(''));
721         $ext->add('app-chanspy', $fc_chanspy, '', new ext_hangup(''));
722       }
723      
724       // Simulate options (ext-test)
725       if ($fc_simu_pstn != '' || $fc_simu_fax != '') {
726         $ext->addInclude('from-internal-additional', 'ext-test'); // Add the include from from-internal
727        
728         if ($fc_simu_pstn != '') {
729           $ext->add('ext-test', $fc_simu_pstn, '', new ext_goto('1', 's', 'from-pstn'));
730         }
731
732         if ($fc_simu_fax != '') {
733           $ext->add('ext-test', $fc_simu_fax, '', new ext_goto('1', 'in_fax', 'ext-fax'));
734         }
735
736         $ext->add('ext-test', 'h', '', new ext_macro('hangupcall'));
737       }
738      
739       /* Always have Fax detection in ext-did, no matter what */
740       $ext->add('ext-did', 'fax', '', new ext_goto('1','in_fax','ext-fax'));
741
742       /* inbound routing extensions */
743       $didlist = core_did_list();
744       if(is_array($didlist)){
745         $catchall = false;
746         $catchall_context='ext-did-catchall';
747         foreach($didlist as $item) {
748           $did = core_did_get($item['extension'],$item['cidnum']);
749           $exten = $did['extension'];
750           $cidnum = $did['cidnum'];
751
752           $exten = (empty($exten)?"s":$exten);
753           $exten = $exten.(empty($cidnum)?"":"/".$cidnum); //if a CID num is defined, add it
754
755           $context = "ext-did";
756
757           $ext->add($context, $exten, '', new ext_setvar('__FROM_DID','${EXTEN}'));
758           // always set callerID name
759           $ext->add($context, $exten, '', new ext_gotoif('$[ "${CALLERID(name)}" != "" ] ','cidok'));
760           $ext->add($context, $exten, '', new ext_setvar('CALLERID(name)','${CALLERID(num)}'));
761           $ext->add($context, $exten, 'cidok', new ext_noop('CallerID is ${CALLERID(all)}'));
762
763           if (!empty($item['mohclass']) && trim($item['mohclass']) != 'default') {
764             $ext->add($context, $exten, '', new ext_setmusiconhold($item['mohclass']));
765             $ext->add($context, $exten, '', new ext_setvar('__MOHCLASS',$item['mohclass']));
766           }
767
768           // If we require RINGING, signal it as soon as we enter.
769           if ($item['ringing'] === "CHECKED") {
770             $ext->add($context, $exten, '', new ext_ringing(''));
771           }
772
773           if ($exten == "s" && $context == "ext-did") { 
774             //if the exten is s, then also make a catchall for undefined DIDs
775             $catchaccount = "_.".(empty($cidnum)?"":"/".$cidnum);
776             if ($catchaccount =="_." && ! $catchall) {
777               $catchall = true;
778               $ext->add($catchall_context, $catchaccount, '', new ext_NoOp('Catch-All DID Match - Found ${EXTEN} - You probably want a DID for this.'));
779               $ext->add($catchall_context, $catchaccount, '', new ext_goto('1','s','ext-did'));
780             }
781           }
782          
783           if ($item['faxexten'] != "default") {
784             $ext->add($context, $exten, '', new ext_setvar('FAX_RX',$item['faxexten']));
785           }
786           if (!empty($item['faxemail'])) {
787             $ext->add($context, $exten, '', new ext_setvar('FAX_RX_EMAIL',$item['faxemail']));
788           }
789           if ($item['answer'] == "1") {
790             $ext->add($context, $exten, '', new ext_answer(''));
791             $ext->add($context, $exten, '', new ext_wait($item['wait']));
792           }
793           if ($item['answer'] == "2") { // NVFaxDetect
794             $ext->add($context, $exten, '', new ext_answer(''));
795             $ext->add($context, $exten, '', new ext_playtones('ring'));
796             $ext->add($context, $exten, '', new ext_nvfaxdetect($item['wait']));
797           }
798           if ($item['privacyman'] == "1") {
799             $ext->add($context, $exten, '', new ext_macro('privacy-mgr'));
800           } else {
801             // if privacymanager is used, this is not necessary as it will not let blocked/anonymous calls through
802             // otherwise, we need to save the caller presence to set it properly if we forward the call back out the pbx
803             // note - the indirect table could go away as of 1.4.20 where it is fixed so that SetCallerPres can take
804             // the raw format.
805             //
806             if (version_compare($version, "1.6", "lt")) {
807               $ext->add($context, $exten, '', new ext_setvar('__CALLINGPRES_SV','${CALLINGPRES_${CALLINGPRES}}'));
808             } else {
809               $ext->add($context, $exten, '', new ext_setvar('__CALLINGPRES_SV','${CALLERPRES()}'));
810             }
811             $ext->add($context, $exten, '', new ext_setcallerpres('allowed_not_screened'));
812           }
813           if (!empty($item['alertinfo'])) {
814             $ext->add($context, $exten, '', new ext_setvar("__ALERT_INFO", str_replace(';', '\;', $item['alertinfo'])));
815           }
816           // Add CID prefix, no need to do checks for existing pre-pends, this is an incoming did so this should
817           // be the first time the CID is manipulated. We set _RGPREFIX which is the same used throughout the different
818           // modules.
819           //
820           // TODO: If/When RGPREFIX is added to trunks, then see code in ringgroups to strip prefix if added here.
821           //
822           // TODO: core FreePBX documentation about this standard. (and probably rename from RGPREFIX to CIDPREFIX)
823           //
824           if (!empty($item['grppre'])) {
825             $ext->add($context, $exten, '', new ext_setvar('_RGPREFIX', $item['grppre']));
826             $ext->add($context, $exten, '', new ext_setvar('CALLERID(name)','${RGPREFIX}${CALLERID(name)}'));
827           }
828          
829           //the goto destination
830           // destination field in 'incoming' database is backwards from what ext_goto expects
831           $goto_context = strtok($did['destination'],',');
832           $goto_exten = strtok(',');
833           $goto_pri = strtok(',');
834           $ext->add($context, $exten, '', new ext_goto($goto_pri,$goto_exten,$goto_context));
835          
836         }
837         // If there's not a catchall, make one with an error message
838         if (!$catchall) {
839           $ext->add($catchall_context, 's', '', new ext_noop("No DID or CID Match"));
840           $ext->add($catchall_context, 's', '', new ext_answer(''));
841           $ext->add($catchall_context, 's', '', new ext_wait('2'));
842           $ext->add($catchall_context, 's', '', new ext_playback('ss-noservice'));
843           $ext->add($catchall_context, 's', '', new ext_sayalpha('${FROM_DID}'));
844           $ext->add($catchall_context, '_[*#X].', '', new ext_setvar('__FROM_DID', '${EXTEN}'));
845           $ext->add($catchall_context, '_[*#X].', '', new ext_noop('Received an unknown call with DID set to ${EXTEN}'));
846           $ext->add($catchall_context, '_[*#X].', '', new ext_goto('1','s','ext-did'));
847         }
848          
849       }
850
851       /* MODIFIED (PL)
852        *
853        * Add Direct DIDs
854        *
855        * This functions creates a new context, ext-did-direct, used to route an incoming DID directly to the specified user.
856        * The purpose is to use when a user has a personal external DID. This keeps it clean and easy to administer.
857        * Any conflict with those routes will depend on which of the two contexts are included first in the extensions.conf file.
858        *
859        * Calls are sent to context from-did-direct though this feature. You must create that context in extenions.conf or
860        * in extensions_custom.conf and it should look something like:
861        *
862        * [from-did-direct]
863        * include => ext-findmefollow
864        * include => ext-local
865        *
866        * This is so that personal ring groups are used if they exist for the direct did and if not, then the local extension.
867        * If the module is not implented, it will just go to the users extension.
868        */
869
870       $directdidlist = core_directdid_list();
871       if(is_array($directdidlist)){
872         $context = "ext-did";
873         if(!is_array($didlist)){
874           /* if not set above, add one here */
875           $ext->add($context, 'fax', '', new ext_goto('1','in_fax','ext-fax'));
876         }
877         foreach($directdidlist as $item) {
878           $exten = $item['directdid'];
879           $ext->add($context, $exten, '', new ext_setvar('__FROM_DID','${EXTEN}'));
880           // always set callerID name
881           $ext->add($context, $exten, '', new ext_gotoif('$[ "${CALLERID(name)}" != "" ] ','cidok'));
882           $ext->add($context, $exten, '', new ext_setvar('CALLERID(name)','${CALLERID(num)}'));
883           $ext->add($context, $exten, 'cidok', new ext_noop('CallerID is ${CALLERID(all)}'));
884
885           if (!empty($item['mohclass']) && trim($item['mohclass']) != 'default') {
886             $ext->add($context, $exten, '', new ext_setmusiconhold($item['mohclass']));
887             $ext->add($context, $exten, '', new ext_setvar('__MOHCLASS',$item['mohclass']));
888           }
889          
890           if ($item['faxexten'] != "default") {
891             $ext->add($context, $exten, '', new ext_setvar('FAX_RX',$item['faxexten']));
892           }
893           if (!empty($item['faxemail'])) {
894             $ext->add($context, $exten, '', new ext_setvar('FAX_RX_EMAIL',$item['faxemail']));
895           }
896           if ($item['answer'] == "1") {
897             $ext->add($context, $exten, '', new ext_answer(''));
898             $ext->add($context, $exten, '', new ext_wait($item['wait']));
899           }
900           if ($item['answer'] == "2") { // NVFaxDetect
901             $ext->add($context, $exten, '', new ext_answer(''));
902             $ext->add($context, $exten, '', new ext_playtones('ring'));
903             $ext->add($context, $exten, '', new ext_nvfaxdetect($item['wait']));
904           }
905           if ($item['privacyman'] == "1") {
906             $ext->add($context, $exten, '', new ext_macro('privacy-mgr'));
907           }
908
909
910           if (!empty($item['didalert'])) {
911             $ext->add($context, $exten, '', new ext_setvar("_ALERT_INFO", str_replace(';', '\;', $item['didalert'])));
912           }
913           $goto_context = 'from-did-direct';
914           $goto_exten = $item['extension'];
915           $goto_pri = 1;
916           $ext->add($context, $exten, '', new ext_goto($goto_pri,$goto_exten,$goto_context));
917
918         }
919       }
920
921       // Now create macro-from-zaptel-nnn for each defined channel to route it to the DID routing
922       // Send it to from-trunk so it is handled as other dids would be handled.
923       //
924       foreach (core_zapchandids_list() as $row) {
925         $channel = $row['channel'];
926         $did     = $row['did'];
927
928         $zap_context = "macro-from-zaptel-{$channel}";
929         $ext->add($zap_context, 's', '', new ext_noop('Entering '.$zap_context.' with DID = ${DID} and setting to: '.$did));
930         $ext->add($zap_context, 's', '', new ext_setvar('__FROM_DID',$did));
931         $ext->add($zap_context, 's', '', new ext_goto('1',$did,'from-trunk'));
932       }
933
934       /* user extensions */
935       $ext->addInclude('from-internal-additional','ext-local');
936
937       // If running in Dynamic mode, this will insert the hints through an Asterisk #exec call.
938       // which require "execincludes=yes" to be set in the [options] section of asterisk.conf
939       //
940       if ($amp_conf['DYNAMICHINTS']) {
941         $ext->addExec('ext-local',$amp_conf['AMPBIN'].'/generate_hints.php');
942       }
943       $userlist = core_users_list();
944       if (is_array($userlist)) {
945         foreach($userlist as $item) {
946           $exten = core_users_get($item[0]);
947           $vm = ((($exten['voicemail'] == "novm") || ($exten['voicemail'] == "disabled") || ($exten['voicemail'] == "")) ? "novm" : $exten['extension']);
948
949           if (isset($exten['ringtimer']) && $exten['ringtimer'] != 0)
950             $ext->add('ext-local', $exten['extension'], '', new ext_setvar('__RINGTIMER',$exten['ringtimer']));
951          
952           $ext->add('ext-local', $exten['extension'], '', new ext_macro('exten-vm',$vm.",".$exten['extension']));
953           $ext->add('ext-local', $exten['extension'], '', new ext_hangup(''));
954          
955           if($vm != "novm") {
956             $ext->add('ext-local', '${VM_PREFIX}'.$exten['extension'], '', new ext_macro('vm',"$vm,DIRECTDIAL"));
957             $ext->add('ext-local', '${VM_PREFIX}'.$exten['extension'], '', new ext_hangup(''));
958             $ext->add('ext-local', 'vmb'.$exten['extension'], '', new ext_macro('vm',"$vm,BUSY"));
959             $ext->add('ext-local', 'vmb'.$exten['extension'], '', new ext_hangup(''));
960             $ext->add('ext-local', 'vmu'.$exten['extension'], '', new ext_macro('vm',"$vm,NOANSWER"));
961             $ext->add('ext-local', 'vmu'.$exten['extension'], '', new ext_hangup(''));
962             $ext->add('ext-local', 'vms'.$exten['extension'], '', new ext_macro('vm',"$vm,NOMESSAGE"));
963             $ext->add('ext-local', 'vms'.$exten['extension'], '', new ext_hangup(''));
964           }
965            
966           // Create the hints if running in normal mode
967           //
968           if (!$amp_conf['DYNAMICHINTS']) {
969             $hint = core_hint_get($exten['extension']);
970             if (!empty($hint)) {
971               $ext->addHint('ext-local', $exten['extension'], $hint);
972             }
973           }
974           if ($exten['sipname']) {
975             $ext->add('ext-local', $exten['sipname'], '', new ext_goto('1',$item[0],'from-internal'));
976           }
977           // Now make a special context for the IVR inclusions of local extension dialing so that
978           // when people use the Queues breakout ability, and break out to someone's extensions, voicemail
979           // works.
980           //
981           $ivr_context = 'from-did-direct-ivr';
982           $ext->add($ivr_context, $exten['extension'],'', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]','dbDel','${BLKVM_OVERRIDE}'));
983           $ext->add($ivr_context, $exten['extension'],'', new ext_setvar('__NODEST', ''));
984           $ext->add($ivr_context, $exten['extension'],'', new ext_goto('1',$exten['extension'],'from-did-direct'));
985           if($vm != "novm") {
986             $ext->add($ivr_context, '${VM_PREFIX}'.$exten['extension'],'', new ext_execif('$["${BLKVM_OVERRIDE}" != ""]','dbDel','${BLKVM_OVERRIDE}'));
987             $ext->add($ivr_context, '${VM_PREFIX}'.$exten['extension'],'', new ext_setvar('__NODEST', ''));
988             $ext->add($ivr_context, '${VM_PREFIX}'.$exten['extension'],'', new ext_macro('vm',"$vm,DIRECTDIAL"));
989           }
990         }
991       }
992
993       // create from-trunk context for each trunk that adds counts to channels
994       //
995       $trunklist = core_trunks_list(true);
996       if (is_array($trunklist)) {
997         foreach ($trunklist as $trunkprops) {
998           if (trim($trunkprops['value']) == 'on') {
999             // value of on is disabled and for zap we don't create a context
1000             continue;
1001           }
1002           switch ($trunkprops['tech']) {
1003             case 'DUNDI':
1004               $macro_name = 'macro-dundi-'.substr($trunkprops['globalvar'],4);
1005               $ext->addSwitch($macro_name,'DUNDI/'.$trunkprops['name']);
1006               $ext->add($macro_name, 's', '', new ext_goto('1','${ARG1}'));
1007             case 'IAX':
1008             case 'IAX2':
1009             case 'SIP':
1010               $trunkgroup = $trunkprops['globalvar'];
1011               $trunkcontext  = "from-trunk-".strtolower($trunkprops['tech'])."-".$trunkprops['name'];
1012               $ext->add($trunkcontext, '_.', '', new ext_setvar('GROUP()',$trunkgroup));
1013               $ext->add($trunkcontext, '_.', '', new ext_goto('1','${EXTEN}','from-trunk'));
1014               break;
1015             default:
1016           }
1017         }
1018       }
1019
1020       /* dialplan globals */
1021       // modules should NOT use the globals table to store anything!
1022       // modules should use $ext->addGlobal("testvar","testval"); in their module_get_config() function instead
1023       // I'm cheating for core functionality - do as I say, not as I do ;-)   
1024
1025       // Auto add these globals to give access to agi scripts and other needs, unless defined in the global table.
1026       //
1027       $amp_conf_globals = array(
1028         "ASTETCDIR",
1029         "ASTMODDIR",
1030         "ASTVARLIBDIR",
1031         "ASTAGIDIR",
1032         "ASTSPOOLDIR",
1033         "ASTRUNDIR",
1034         "ASTLOGDIR",
1035         "CWINUSEBUSY",
1036         "AMPMGRUSER",
1037         "AMPMGRPASS"
1038       );
1039
1040       $sql = "SELECT * FROM globals";
1041       $globals = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1042       foreach($globals as $global) {
1043         $ext->addGlobal($global['variable'],$global['value']);
1044
1045         // now if for some reason we have a variable in the global table
1046         // that is in our $amp_conf_globals list, then remove it so we
1047         // don't duplicate, the sql table will take precedence
1048         //
1049         if (array_key_exists($global['variable'],$amp_conf_globals)) {
1050           $rm_keys = array_keys($amp_conf_globals,$global['variable']);
1051           foreach ($rm_keys as $index) {
1052             unset($amp_conf_globals[$index]);
1053           }
1054         }
1055       }
1056       foreach ($amp_conf_globals as $global) {
1057         if (isset($amp_conf[$global])) {
1058           $value = $amp_conf[$global];
1059           if ($value === true || $value === false) {
1060             $value = ($value) ? 'true':'false';
1061           }
1062           $ext->addGlobal($global, $value);
1063           out("Added to globals: $global = $value");
1064         }
1065       }
1066       // Put the asterisk version in a global for agi etc.
1067       $ext->addGlobal('ASTVERSION', $version);
1068
1069       // Create CallingPresTable to deal with difference that ${CALINGPRES} returns vs. what
1070       // SetCallerPres() accepts. This is a workaround that gets resolved in 1.6 where
1071       // function CALLINGPRES() is consistent.
1072       // This should be fixed in 1.4.20 but for now we keep it in until 1.6
1073       //
1074       if (version_compare($version, "1.6", "lt")) {
1075         $ext->addGlobal('CALLINGPRES_0', 'allowed_not_screened');
1076         $ext->addGlobal('CALLINGPRES_1', 'allowed_passed_screen');
1077         $ext->addGlobal('CALLINGPRES_2', 'allowed_failed_screen');
1078         $ext->addGlobal('CALLINGPRES_3', 'allowed');
1079         $ext->addGlobal('CALLINGPRES_32', 'prohib_not_screened');
1080         $ext->addGlobal('CALLINGPRES_33', 'prohib_passed_screen');
1081         $ext->addGlobal('CALLINGPRES_34', 'prohib_failed_screen');
1082         $ext->addGlobal('CALLINGPRES_35', 'prohib');
1083         $ext->addGlobal('CALLINGPRES_67', 'unavailable');
1084       }
1085
1086       /* outbound routes */
1087       // modules should use their own table for storage (and module_get_config() to add dialplan)
1088       // modules should NOT use the extension table to store anything!
1089       $sql = "SELECT application FROM extensions where context = 'outbound-allroutes' ORDER BY application";
1090       $outrts = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1091       $ext->addInclude('from-internal-additional','outbound-allroutes');
1092       $ext->add('outbound-allroutes', 'foo', '', new ext_noop('bar'));
1093       foreach($outrts as $outrt) {
1094         $ext->addInclude('outbound-allroutes',$outrt['application']);
1095         $sql = "SELECT * FROM extensions where context = '".$outrt['application']."' ORDER BY extension, CAST(priority AS UNSIGNED) ASC";
1096         $thisrt = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1097         $lastexten = false;
1098         foreach($thisrt as $exten) {
1099           //if emergencyroute, then set channel var
1100           if(strpos($exten['args'],"EMERGENCYROUTE") !== false)
1101             $ext->add($outrt['application'], $exten['extension'], '', new ext_setvar("EMERGENCYROUTE",substr($exten['args'],15)));
1102           if(strpos($exten['args'],"INTRACOMPANYROUTE") !== false)
1103             $ext->add($outrt['application'], $exten['extension'], '', new ext_setvar("INTRACOMPANYROUTE",substr($exten['args'],18)));
1104           // Don't set MOHCLASS if already set, threre may be a feature code that overrode it
1105           if(strpos($exten['args'],"MOHCLASS") !== false)
1106             $ext->add($outrt['application'], $exten['extension'], '', new ext_setvar("MOHCLASS", '${IF($["x${MOHCLASS}"="x"]?'.substr($exten['args'],9).':${MOHCLASS})}' ));
1107           if(strpos($exten['args'],"dialout-trunk") !== false || strpos($exten['args'],"dialout-enum") !== false || strpos($exten['args'],"dialout-dundi") !== false) {
1108             if ($exten['extension'] !== $lastexten) {
1109
1110               // If NODEST is set, clear it. No point in remembering since dialout-trunk will just end in the
1111               // bit bucket. But if answered by an outside line with transfer capability, we want NODEST to be
1112               // clear so a subsequent transfer to an internal extension works and goes to voicmail or other
1113               // destinations.
1114               //
1115               // Then do one call to user-callerid and record-enable instead of each time as in the past
1116               //
1117               $ext->add($outrt['application'], $exten['extension'], '', new ext_macro('user-callerid,SKIPTTL'));
1118               $ext->add($outrt['application'], $exten['extension'], '', new ext_setvar("_NODEST",""));
1119               $ext->add($outrt['application'], $exten['extension'], '', new ext_macro('record-enable,${AMPUSER},OUT'));
1120               $lastexten = $exten['extension'];
1121             }
1122             $ext->add($outrt['application'], $exten['extension'], '', new ext_macro($exten['args']));
1123           }
1124           if(strpos($exten['args'],"outisbusy") !== false)
1125             $ext->add($outrt['application'], $exten['extension'], '', new ext_macro("outisbusy"));
1126         }
1127       }
1128
1129       general_generate_indications();
1130
1131       // "blackhole" destinations
1132       $ext->add('app-blackhole', 'hangup', '', new ext_noop('Blackhole Dest: Hangup'));
1133       $ext->add('app-blackhole', 'hangup', '', new ext_hangup());
1134
1135       $ext->add('app-blackhole', 'zapateller', '', new ext_noop('Blackhole Dest: Play SIT Tone'));
1136       $ext->add('app-blackhole', 'zapateller', '', new ext_answer());
1137       $ext->add('app-blackhole', 'zapateller', '', new ext_zapateller());
1138       // Should hangup ?
1139       // $ext->add('app-blackhole', 'zapateller', '', new ext_hangup());
1140          
1141       $ext->add('app-blackhole', 'musiconhold', '', new ext_noop('Blackhole Dest: Put caller on hold forever'));
1142       $ext->add('app-blackhole', 'musiconhold', '', new ext_answer());
1143       $ext->add('app-blackhole', 'musiconhold', '', new ext_musiconhold());
1144
1145       $ext->add('app-blackhole', 'congestion', '', new ext_noop('Blackhole Dest: Congestion'));
1146       $ext->add('app-blackhole', 'congestion', '', new ext_answer());
1147       $ext->add('app-blackhole', 'congestion', '', new ext_playtones('congestion'));
1148       $ext->add('app-blackhole', 'congestion', '', new ext_congestion());
1149       $ext->add('app-blackhole', 'congestion', '', new ext_hangup());
1150
1151       $ext->add('app-blackhole', 'busy', '', new ext_noop('Blackhole Dest: Busy'));
1152       $ext->add('app-blackhole', 'busy', '', new ext_answer());
1153       $ext->add('app-blackhole', 'busy', '', new ext_playtones('busy'));
1154       $ext->add('app-blackhole', 'busy', '', new ext_busy());
1155       $ext->add('app-blackhole', 'busy', '', new ext_hangup());
1156
1157       $ext->add('app-blackhole', 'ring', '', new ext_noop('Blackhole Dest: Ring'));
1158       $ext->add('app-blackhole', 'ring', '', new ext_answer());
1159       $ext->add('app-blackhole', 'ring', '', new ext_playtones('ring'));
1160       $ext->add('app-blackhole', 'ring', '', new ext_wait(300));
1161       $ext->add('app-blackhole', 'ring', '', new ext_hangup());
1162
1163       if ($amp_conf['AMPBADNUMBER'] !== false) {
1164         $context = 'bad-number';
1165         $exten = '_X.';
1166         $ext->add($context, $exten, '', new extension('ResetCDR()'));
1167         $ext->add($context, $exten, '', new extension('NoCDR()'));
1168         $ext->add($context, $exten, '', new ext_wait('1'));
1169         $ext->add($context, $exten, '', new ext_playback('silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer'));
1170         $ext->add($context, $exten, '', new ext_wait('1'));
1171         $ext->add($context, $exten, '', new ext_congestion('20'));
1172         $ext->add($context, $exten, '', new ext_hangup());
1173
1174         $exten = '_*.';
1175         $ext->add($context, $exten, '', new extension('ResetCDR()'));
1176         $ext->add($context, $exten, '', new extension('NoCDR()'));
1177         $ext->add($context, $exten, '', new ext_wait('1'));
1178         $ext->add($context, $exten, '', new ext_playback('silence/1&feature-not-avail-line&silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer'));
1179         $ext->add($context, $exten, '', new ext_wait('1'));
1180         $ext->add($context, $exten, '', new ext_congestion('20'));
1181         $ext->add($context, $exten, '', new ext_hangup());
1182       }
1183
1184       $context = 'macro-dialout-trunk';
1185       $exten = 's';
1186      
1187       /*
1188        * dialout using a trunk, using pattern matching (don't strip any prefix)
1189        * arg1 = trunk number, arg2 = number, arg3 = route password
1190        *
1191        * MODIFIED (PL)
1192        *
1193        * Modified both Dial() commands to include the new TRUNK_OPTIONS from the general
1194        * screen of AMP
1195        */
1196       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
1197       $ext->add($context, $exten, '', new ext_execif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'Authenticate', '${ARG3}'));
1198       $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTDISABLE_${DIAL_TRUNK}}" = "xon"]', 'disabletrunk,1'));
1199       $ext->add($context, $exten, '', new ext_set('DIAL_NUMBER', '${ARG2}')); // fixlocalprefix depends on this
1200       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${DIAL_OPTIONS}')); // will be reset to TRUNK_OPTIONS if not intra-company
1201       $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
1202       $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]', 'nomax'));
1203       $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}} ]', 'chanfull'));
1204       $ext->add($context, $exten, 'nomax', new ext_gotoif('$["${INTRACOMPANYROUTE}" = "YES"]', 'skipoutcid'));  // Set to YES if treated like internal
1205       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${TRUNK_OPTIONS}'));
1206       $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${DIAL_TRUNK}'));
1207       $ext->add($context, $exten, 'skipoutcid', new ext_agi('fixlocalprefix'));  // this sets DIAL_NUMBER to the proper dial string for this trunk
1208       $ext->add($context, $exten, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));  // OUTNUM is the final dial number
1209       $ext->add($context, $exten, '', new ext_set('custom', '${CUT(OUT_${DIAL_TRUNK},:,1)}'));  // Custom trunks are prefixed with "AMP:"
1210    
1211       // Back to normal processing, whether intracompany or not.
1212       // But add the macro-setmusic if we don't want music on this outbound call
1213       $ext->add($context, $exten, '', new ext_gotoif('$[$["${MOHCLASS}" = "default"] | $["foo${MOHCLASS}" = "foo"]]', 'gocall'));  // Set to YES if we should pump silence
1214       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', 'M(setmusic^${MOHCLASS})${DIAL_TRUNK_OPTIONS}'));  // set MoH or off
1215    
1216       // This macro call will always be blank and is provided as a hook for customization required prior to making a call
1217       // such as adding SIP header information or other requirements. All the channel variables from above are present
1218      
1219       $ext->add($context, $exten, 'gocall', new ext_macro('dialout-trunk-predial-hook'));
1220       $ext->add($context, $exten, '', new ext_gotoif('$["${PREDIAL_HOOK_RET}" = "BYPASS"]', 'bypass,1'));
1221    
1222       $ext->add($context, $exten, '', new ext_gotoif('$["${custom}" = "AMP"]', 'customtrunk'));
1223       $ext->add($context, $exten, '', new ext_dial('${OUT_${DIAL_TRUNK}}/${OUTNUM}', '300,${DIAL_TRUNK_OPTIONS}'));  // Regular Trunk Dial
1224       $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
1225      
1226       $ext->add($context, $exten, 'customtrunk', new ext_set('pre_num', '${CUT(OUT_${DIAL_TRUNK},$,1)}'));
1227       $ext->add($context, $exten, '', new ext_set('the_num', '${CUT(OUT_${DIAL_TRUNK},$,2)}'));  // this is where we expect to find string OUTNUM
1228       $ext->add($context, $exten, '', new ext_set('post_num', '${CUT(OUT_${DIAL_TRUNK},$,3)}'));
1229       $ext->add($context, $exten, '', new ext_gotoif('$["${the_num}" = "OUTNUM"]', 'outnum', 'skipoutnum'));  // if we didn't find "OUTNUM", then skip to Dial
1230       $ext->add($context, $exten, 'outnum', new ext_set('the_num', '${OUTNUM}'));  // replace "OUTNUM" with the actual number to dial
1231       $ext->add($context, $exten, 'skipoutnum', new ext_dial('${pre_num:4}${the_num}${post_num}', '300,${DIAL_TRUNK_OPTIONS}'));
1232       $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
1233      
1234       $ext->add($context, $exten, 'chanfull', new ext_noop('max channels used up'));
1235    
1236       $exten = 's-BUSY';
1237       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting BUSY - giving up'));
1238       $ext->add($context, $exten, '', new ext_playtones('busy'));
1239       $ext->add($context, $exten, '', new ext_busy(20));
1240    
1241       $exten = 's-NOANSWER';
1242       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting NOANSWER - giving up'));
1243       $ext->add($context, $exten, '', new ext_playtones('congestion'));
1244       $ext->add($context, $exten, '', new ext_congestion(20));
1245    
1246       $exten = 's-CANCEL';
1247       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting CANCEL - giving up'));
1248       $ext->add($context, $exten, '', new ext_playtones('congestion'));
1249       $ext->add($context, $exten, '', new ext_congestion(20));
1250    
1251       $exten = '_s-.';
1252       $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTFAIL_${ARG1}}" = "x"]', 'noreport'));
1253       $ext->add($context, $exten, '', new ext_agi('${OUTFAIL_${ARG1}}'));
1254       $ext->add($context, $exten, 'noreport', new ext_noop('TRUNK Dial failed due to ${DIALSTATUS} - failing through to other trunks'));
1255      
1256       $ext->add($context, 'disabletrunk', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} DISABLED - falling through to next trunk'));
1257       $ext->add($context, 'bypass', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} BYPASSING because dialout-trunk-predial-hook'));
1258    
1259       $ext->add($context, 'h', '', new ext_macro('hangupcall'));
1260
1261
1262
1263
1264       $context = 'macro-dialout-dundi';
1265       $exten = 's';
1266      
1267       /*
1268        * Dialout Dundi Trunk
1269        */
1270       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
1271       $ext->add($context, $exten, '', new ext_execif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'Authenticate', '${ARG3}'));
1272       $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTDISABLE_${DIAL_TRUNK}}" = "xon"]', 'disabletrunk,1'));
1273       $ext->add($context, $exten, '', new ext_set('DIAL_NUMBER', '${ARG2}')); // fixlocalprefix depends on this
1274       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${DIAL_OPTIONS}')); // will be reset to TRUNK_OPTIONS if not intra-company
1275       $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${DIAL_TRUNK}'));
1276       $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${DIAL_TRUNK}}foo" = "foo"]', 'nomax'));
1277       $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${DIAL_TRUNK})} >= ${OUTMAXCHANS_${DIAL_TRUNK}} ]', 'chanfull'));
1278       $ext->add($context, $exten, 'nomax', new ext_gotoif('$["${INTRACOMPANYROUTE}" = "YES"]', 'skipoutcid'));  // Set to YES if treated like internal
1279       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', '${TRUNK_OPTIONS}'));
1280       $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${DIAL_TRUNK}'));
1281       $ext->add($context, $exten, 'skipoutcid', new ext_agi('fixlocalprefix'));  // this sets DIAL_NUMBER to the proper dial string for this trunk
1282       $ext->add($context, $exten, '', new ext_set('OUTNUM', '${OUTPREFIX_${DIAL_TRUNK}}${DIAL_NUMBER}'));  // OUTNUM is the final dial number
1283
1284       // Back to normal processing, whether intracompany or not.
1285       // But add the macro-setmusic if we don't want music on this outbound call
1286       $ext->add($context, $exten, '', new ext_gotoif('$[$["${MOHCLASS}" = "default"] | $["foo${MOHCLASS}" = "foo"]]', 'gocall'));  // Set to YES if we should pump silence
1287       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK_OPTIONS', 'M(setmusic^${MOHCLASS})${DIAL_TRUNK_OPTIONS}'));  // set MoH or off
1288    
1289       // This macro call will always be blank and is provided as a hook for customization required prior to making a call
1290       // such as adding SIP header information or other requirements. All the channel variables from above are present
1291      
1292       $ext->add($context, $exten, 'gocall', new ext_macro('dialout-dundi-predial-hook'));
1293       $ext->add($context, $exten, '', new ext_gotoif('$["${PREDIAL_HOOK_RET}" = "BYPASS"]', 'bypass,1'));
1294    
1295       $ext->add($context, $exten, '', new ext_gotoif('$["${custom}" = "AMP"]', 'customtrunk'));
1296
1297       $ext->add($context, $exten, '', new ext_macro('dundi-${DIAL_TRUNK}','${OUTNUM}'));
1298       $ext->add($context, $exten, '', new ext_goto(1, 's-${DIALSTATUS}'));
1299      
1300       $ext->add($context, $exten, 'chanfull', new ext_noop('max channels used up'));
1301    
1302       $exten = 's-BUSY';
1303       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting BUSY - giving up'));
1304       $ext->add($context, $exten, '', new ext_playtones('busy'));
1305       $ext->add($context, $exten, '', new ext_busy(20));
1306    
1307       $exten = 's-NOANSWER';
1308       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting NOANSWER - giving up'));
1309       $ext->add($context, $exten, '', new ext_playtones('congestion'));
1310       $ext->add($context, $exten, '', new ext_congestion(20));
1311    
1312       $exten = 's-CANCEL';
1313       $ext->add($context, $exten, '', new ext_noop('Dial failed due to trunk reporting CANCEL - giving up'));
1314       $ext->add($context, $exten, '', new ext_playtones('congestion'));
1315       $ext->add($context, $exten, '', new ext_congestion(20));
1316    
1317       $exten = '_s-.';
1318       $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTFAIL_${ARG1}}" = "x"]', 'noreport'));
1319       $ext->add($context, $exten, '', new ext_agi('${OUTFAIL_${ARG1}}'));
1320       $ext->add($context, $exten, 'noreport', new ext_noop('TRUNK Dial failed due to ${DIALSTATUS} - failing through to other trunks'));
1321      
1322       $ext->add($context, 'disabletrunk', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} DISABLED - falling through to next trunk'));
1323       $ext->add($context, 'bypass', '', new ext_noop('TRUNK: ${OUT_${DIAL_TRUNK}} BYPASSING because dialout-dundi-predial-hook'));
1324    
1325       $ext->add($context, 'h', '', new ext_macro('hangupcall'));
1326
1327
1328
1329       /*
1330        * sets the callerid of the device to that of the logged in user
1331        *
1332        * ${AMPUSER} is set upon return to the real user despite any aliasing that may
1333        * have been set as a result of the AMPUSER/<nnn>/cidnum field. This is used by
1334        * features like DND, CF, etc. to set the proper structure on aliased instructions
1335        */
1336       $context = 'macro-user-callerid';
1337       $exten = 's';
1338      
1339       $ext->add($context, $exten, '', new ext_noop('user-callerid: ${CALLERID(name)} ${CALLERID(number)}'));
1340              
1341       // make sure AMPUSER is set if it doesn't get set below     
1342       $ext->add($context, $exten, '', new ext_set('AMPUSER', '${IF($["foo${AMPUSER}" = "foo"]?${CALLERID(number)}:${AMPUSER})}'));
1343       $ext->add($context, $exten, '', new ext_gotoif('$["${CHANNEL:0:5}" = "Local"]', 'report'));
1344       $ext->add($context, $exten, '', new ext_execif('$["${REALCALLERIDNUM:1:2}" = ""]', 'Set', 'REALCALLERIDNUM=${CALLERID(number)}'));
1345       $ext->add($context, $exten, 'start', new ext_noop('REALCALLERIDNUM is ${REALCALLERIDNUM}'));
1346       $ext->add($context, $exten, '', new ext_set('AMPUSER', '${DB(DEVICE/${REALCALLERIDNUM}/user)}'));
1347       $ext->add($context, $exten, '', new ext_set('AMPUSERCIDNAME', '${DB(AMPUSER/${AMPUSER}/cidname)}'));
1348       $ext->add($context, $exten, '', new ext_gotoif('$["x${AMPUSERCIDNAME:1:2}" = "x"]', 'report'));
1349
1350       // user may masquerade as a different user internally, so set the internal cid as indicated
1351       // but keep the REALCALLERID which is used to determine their true identify and lookup info
1352       // during outbound calls.
1353       $ext->add($context, $exten, '', new ext_set('AMPUSERCID', '${IF($["${DB_EXISTS(AMPUSER/${AMPUSER}/cidnum)}" = "1"]?${DB_RESULT}:${AMPUSER})}'));
1354       $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '"${AMPUSERCIDNAME}" <${AMPUSERCID}>'));
1355       $ext->add($context, $exten, '', new ext_set('REALCALLERIDNUM', '${DB(DEVICE/${REALCALLERIDNUM}/user)}'));
1356       if (version_compare($version, "1.4", "ge")) {
1357         $ext->add($context, $exten, '', new ext_execif('$["${DB(AMPUSER/${AMPUSER}/language)}" != ""]', 'Set', 'CHANNEL(language)=${DB(AMPUSER/${AMPUSER}/language)}'));
1358       } else {
1359         $ext->add($context, $exten, '', new ext_execif('$["${DB(AMPUSER/${AMPUSER}/language)}" != ""]', 'Set', 'LANGUAGE()=${DB(AMPUSER/${AMPUSER}/language)}'));
1360       }
1361       $ext->add($context, $exten, 'report', new ext_noop('TTL: ${TTL} ARG1: ${ARG1}'));
1362       $ext->add($context, $exten, '', new ext_gotoif('$[ "${ARG1}" = "SKIPTTL" ]', 'continue'));
1363       $ext->add($context, $exten, 'report2', new ext_set('__TTL', '${IF($["foo${TTL}" = "foo"]?64:$[ ${TTL} - 1 ])}'));
1364       $ext->add($context, $exten, '', new ext_gotoif('$[ ${TTL} > 0 ]', 'continue'));
1365       $ext->add($context, $exten, '', new ext_wait('${RINGTIMER}'));  // wait for a while, to give it a chance to be picked up by voicemail
1366       $ext->add($context, $exten, '', new ext_answer());
1367       $ext->add($context, $exten, '', new ext_wait('2'));
1368       $ext->add($context, $exten, '', new ext_playback('im-sorry&an-error-has-occured&with&call-forwarding'));
1369       $ext->add($context, $exten, '', new ext_macro('hangupcall'));
1370       $ext->add($context, $exten, '', new ext_congestion(20));
1371       $ext->add($context, $exten, 'continue', new ext_noop('Using CallerID ${CALLERID(all)}'));
1372       $ext->add($context, 'h', '', new ext_macro('hangupcall'));
1373      
1374       /*
1375        * arg1 = trunk number, arg2 = number
1376        *
1377        * Re-written to use enumlookup.agi
1378        */
1379  
1380       $context = 'macro-dialout-enum';
1381       $exten = 's';
1382  
1383       $ext->add($context, $exten, '', new ext_execif('$[$["${ARG3}" != ""] & $["${DB(AMPUSER/${AMPUSER}/pinless)}" != "NOPASSWD"]]', 'Authenticate', '${ARG3}'));
1384       $ext->add($context, $exten, '', new ext_macro('outbound-callerid', '${ARG1}'));
1385       $ext->add($context, $exten, '', new ext_set('OUTBOUND_GROUP', 'OUT_${ARG1}'));
1386       $ext->add($context, $exten, '', new ext_gotoif('$["${OUTMAXCHANS_${ARG1}}foo" = "foo"]', 'nomax'));
1387       $ext->add($context, $exten, '', new ext_gotoif('$[ ${GROUP_COUNT(OUT_${ARG1})} >= ${OUTMAXCHANS_${ARG1}} ]', 'nochans'));
1388       $ext->add($context, $exten, 'nomax', new ext_set('DIAL_NUMBER', '${ARG2}'));
1389       $ext->add($context, $exten, '', new ext_set('DIAL_TRUNK', '${ARG1}'));
1390       $ext->add($context, $exten, '', new ext_agi('fixlocalprefix'));  // this sets DIAL_NUMBER to the proper dial string for this trunk
1391       //  Replacement for asterisk's ENUMLOOKUP function
1392       $ext->add($context, $exten, '', new ext_agi('enumlookup.agi'));
1393       // Now we have the variable DIALARR set to a list of URI's that can be called, in order of priority
1394       // Loop through them trying them in order.
1395       $ext->add($context, $exten, 'dialloop', new ext_gotoif('$["foo${DIALARR}"="foo"]', 'end'));
1396       $ext->add($context, $exten, '', new ext_set('TRYDIAL', '${CUT(DIALARR,%,1)}'));
1397       $ext->add($context, $exten, '', new ext_set('DIALARR', '${CUT(DIALARR,%,2-)}'));
1398       $ext->add($context, $exten, '', new ext_dial('${TRYDIAL}', ''));
1399       $ext->add($context, $exten, '', new ext_noop('Dial exited in macro-enum-dialout with ${DIALSTATUS}'));
1400       // Now, if we're still here, that means the Dial failed for some reason.
1401       // If it's CONGESTION or CHANUNAVAIL we want to try again on a different
1402       // different channel. If there's no more left, the dialloop tag will exit.
1403       $ext->add($context, $exten, '', new ext_gotoif('$[ $[ "${DIALSTATUS}" = "CHANUNAVAIL" ] | $[ "${DIALSTATUS}" = "CONGESTION" ] ]', 'dialloop'));
1404       // If we're here, then it's BUSY or NOANSWER or something and well, deal with it.
1405       $ext->add($context, $exten, 'dialfailed', new ext_goto(1, 's-${DIALSTATUS}'));
1406       // Here are the exit points for the macro.
1407       $ext->add($context, $exten, 'nochans', new ext_noop('max channels used up'));
1408       $ext->add($context, $exten, 'end', new ext_noop('Exiting macro-dialout-enum'));
1409       $ext->add($context, 's-BUSY', '', new ext_noop('Trunk is reporting BUSY'));
1410       $ext->add($context, 's-BUSY', '', new ext_busy(20));
1411       $ext->add($context, '_s-.', '', new ext_noop('Dial failed due to ${DIALSTATUS}'));     
1412      
1413       /*
1414        * overrides callerid out trunks
1415        * arg1 is trunk
1416        * macro-user-callerid should be called _before_ using this macro
1417        */
1418
1419       $context = 'macro-outbound-callerid';
1420       $exten = 's';
1421      
1422       // If we modified the caller presence, set it back. This allows anonymous calls to be internally prepended but keep
1423       // their status if forwarded back out. Not doing this can result in the trunk CID being displayed vs. 'blocked call'
1424       //
1425       if (version_compare($version, "1.6", "lt")) {
1426         $ext->add($context, $exten, '', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'SetCallerPres', '${CALLINGPRES_SV}'));
1427       } else {
1428         $ext->add($context, $exten, '', new ext_execif('$["${CALLINGPRES_SV}" != ""]', 'Set', 'CALLERPRES()=${CALLINGPRES_SV}'));
1429       }
1430
1431       // Keep the original CallerID number, for failover to the next trunk.
1432       $ext->add($context, $exten, '', new ext_gotoif('$["${REALCALLERIDNUM:1:2}" != ""]', 'start'));
1433       $ext->add($context, $exten, '', new ext_set('REALCALLERIDNUM', '${CALLERID(number)}'));
1434       $ext->add($context, $exten, 'start', new ext_noop('REALCALLERIDNUM is ${REALCALLERIDNUM}'));
1435
1436       // If this came through a ringgroup or CF, then we want to retain original CID unless
1437       // OUTKEEPCID_${trunknum} is set.
1438       // Save then CIDNAME while it is still intact in case we end up sending out this same CID
1439       $ext->add($context, $exten, '', new ext_gotoif('$["${KEEPCID}" != "TRUE"]', 'normcid'));  // Set to TRUE if coming from ringgroups, CF, etc.
1440       $ext->add($context, $exten, '', new ext_gotoif('$["x${OUTKEEPCID_${ARG1}}" = "xon"]', 'normcid'));
1441       $ext->add($context, $exten, '', new ext_gotoif('$["foo${REALCALLERIDNUM}" = "foo"]', 'normcid'));  // if not set to anything, go through normal processing
1442       $ext->add($context, $exten, '', new ext_set('USEROUTCID', '${REALCALLERIDNUM}'));
1443       //$ext->add($context, $exten, '', new ext_set('REALCALLERIDNAME', '${CALLERID(name)}'));
1444
1445       // We now have to make sure the CID is valid. If we find an AMPUSER with the same CID, we assume it is an internal
1446       // call (would be quite a conincidence if not) and go through the normal processing to get that CID. If a device
1447       // is set for this CID, then it must be internal
1448       // If we end up using USEROUTCID at the end, it may still be the REALCALLERIDNUM we saved above. That is determined
1449       // if the two are equal, AND there is no CALLERID(name) present since it has been removed by the CALLERID(all)=${USEROUTCID}
1450       // setting. If this is the case, then we put the orignal name back in to send out. Although the CNAME is not honored by most
1451       // carriers, there are cases where it is so this preserves that information to be used by those carriers who do honor it.
1452       $ext->add($context, $exten, '', new ext_gotoif('$["foo${DB(AMPUSER/${REALCALLERIDNUM}/device)}" = "foo"]', 'bypass', 'normcid'));
1453
1454       $ext->add($context, $exten, 'normcid', new ext_set('USEROUTCID', '${DB(AMPUSER/${REALCALLERIDNUM}/outboundcid)}'));
1455       $ext->add($context, $exten, 'bypass', new ext_set('EMERGENCYCID', '${DB(DEVICE/${REALCALLERIDNUM}/emergency_cid)}'));
1456       $ext->add($context, $exten, '', new ext_set('TRUNKOUTCID', '${OUTCID_${ARG1}}'));
1457       $ext->add($context, $exten, '', new ext_gotoif('$["${EMERGENCYROUTE:1:2}" = ""]', 'trunkcid'));  // check EMERGENCY ROUTE
1458       $ext->add($context, $exten, '', new ext_gotoif('$["${EMERGENCYCID:1:2}" = ""]', 'trunkcid'));  // empty EMERGENCY CID, so default back to trunk
1459       $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '${EMERGENCYCID}'));  // emergency cid for device
1460       $ext->add($context, $exten, '', new ext_goto('report'));
1461       $ext->add($context, $exten, 'trunkcid', new ext_gotoif('$["${TRUNKOUTCID:1:2}" = ""]', 'usercid'));  // check for CID override for trunk (global var)
1462       $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '${TRUNKOUTCID}'));
1463       $ext->add($context, $exten, 'usercid', new ext_gotoif('$["${USEROUTCID:1:2}" = ""]', 'report'));  // check CID override for extension
1464       $ext->add($context, $exten, '', new ext_set('CALLERID(all)', '${USEROUTCID}'));
1465       //$ext->add($context, $exten, '', new ext_gotoif('$["x${CALLERID(name)}"!="xhidden"]', 'checkname', 'hidecid'));  // check CID blocking for extension
1466       $ext->add($context, $exten, '', new ext_gotoif('$["x${CALLERID(name)}"!="xhidden"]', 'report', 'hidecid'));  // check CID blocking for extension
1467       $ext->add($context, $exten, 'hidecid', new ext_setcallerpres('prohib_passed_screen'));  // Only works with ISDN (T1/E1/BRI)
1468       //$ext->add($context, $exten, 'checkname', new ext_execif('$[ $[ "${CALLERID(number)}" = "${REALCALLERIDNUM}" ] & $[ "${CALLERID(name)}" = "" ] ]', 'Set', 'CALLERID(name)=${REALCALLERIDNAME}'));
1469       $ext->add($context, $exten, 'report', new ext_noop('CallerID set to ${CALLERID(all)}'));     
1470
1471      
1472       /*
1473        * Adds a dynamic agent/member to a Queue
1474        * Prompts for call-back number - in not entered, uses CIDNum
1475        */
1476
1477       $context = 'macro-agent-add';
1478       $exten = 's';
1479      
1480       $ext->add($context, $exten, '', new ext_wait(1));
1481       $ext->add($context, $exten, '', new ext_macro('user-callerid', 'SKIPTTL'));
1482       $ext->add($context, $exten, 'a3', new ext_read('CALLBACKNUM', 'agent-login'));  // get callback number from user
1483       $ext->add($context, $exten, '', new ext_gotoif('$["${CALLBACKNUM}" != ""]', 'a7'));  // if user just pressed # or timed out, use cidnum
1484       $ext->add($context, $exten, 'a5', new ext_set('CALLBACKNUM', '${AMPUSER}'));
1485       $ext->add($context, $exten, '', new ext_execif('$["${CALLBACKNUM}" = ""]', 'Set', 'CALLBACKNUM=${CALLERID(number)}'));
1486       $ext->add($context, $exten, '', new ext_gotoif('$["${CALLBACKNUM}" = ""]', 'a3'));  // if still no number, start over
1487       $ext->add($context, $exten, 'a7', new ext_gotoif('$["${CALLBACKNUM}" = "${ARG1}"]', 'invalid'));  // Error, they put in the queue number
1488       $ext->add($context, $exten, '', new ext_execif('$["${ARG2}" != ""]', 'Authenticate', '${ARG2}'));
1489       $ext->add($context, $exten, 'a9', new ext_addqueuemember('${ARG1}', 'Local/${CALLBACKNUM}@from-internal/n'));  // using chan_local allows us to have agents over trunks
1490       $ext->add($context, $exten, '', new ext_userevent('Agentlogin', 'Agent: ${CALLBACKNUM}'));
1491       $ext->add($context, $exten, '', new ext_wait(1));
1492       $ext->add($context, $exten, '', new ext_playback('agent-loginok&with&extension'));
1493       $ext->add($context, $exten, '', new ext_saydigits('${CALLBACKNUM}'));
1494       $ext->add($context, $exten, '', new ext_hangup());
1495       $ext->add($context, $exten, '', new ext_macroexit());
1496       $ext->add($context, $exten, 'invalid', new ext_playback('pbx-invalid'));
1497       $ext->add($context, $exten, '', new ext_goto('a3'));
1498
1499       /*
1500        * Removes a dynamic agent/member from a Queue
1501        * Prompts for call-back number - in not entered, uses CIDNum
1502        */
1503
1504       $context = 'macro-agent-del';
1505      
1506       $ext->add($context, $exten, '', new ext_wait(1));
1507       $ext->add($context, $exten, '', new ext_macro('user-callerid', 'SKIPTTL'));
1508       $ext->add($context, $exten, 'a3', new ext_read('CALLBACKNUM', 'agent-logoff'));  // get callback number from user
1509       $ext->add($context, $exten, '', new ext_gotoif('$["${CALLBACKNUM}" = ""]', 'a5', 'a7'));  // if user just pressed # or timed out, use cidnum
1510       $ext->add($context, $exten, 'a5', new ext_set('CALLBACKNUM', '${AMPUSER}'));
1511       $ext->add($context, $exten, '', new ext_execif('$["${CALLBACKNUM}" = ""]', 'Set', 'CALLBACKNUM=${CALLERID(number)}'));
1512       $ext->add($context, $exten, '', new ext_gotoif('$["${CALLBACKNUM}" = ""]', 'a3'));  // if still no number, start over
1513       $ext->add($context, $exten, 'a7', new ext_removequeuemember('${ARG1}', 'Local/${CALLBACKNUM}@from-internal/n'));
1514       $ext->add($context, $exten, '', new ext_userevent('RefreshQueue'));
1515       $ext->add($context, $exten, '', new ext_wait(1));
1516       $ext->add($context, $exten, '', new ext_playback('agent-loggedoff'));
1517       $ext->add($context, $exten, '', new ext_hangup());
1518
1519       $context = 'macro-systemrecording';
1520      
1521       $ext->add($context, 's', '', new ext_goto(1, '${ARG1}'));
1522      
1523       $exten = 'dorecord';
1524      
1525       $ext->add($context, $exten, '', new ext_record('/tmp/${AMPUSER}-ivrrecording:wav'));
1526       $ext->add($context, $exten, '', new ext_wait(1));
1527       $ext->add($context, $exten, '', new ext_goto(1, 'confmenu'));
1528
1529       $exten = 'docheck';
1530      
1531       $ext->add($context, $exten, '', new ext_playback('/tmp/${AMPUSER}-ivrrecording'));
1532       $ext->add($context, $exten, '', new ext_wait(1));
1533       $ext->add($context, $exten, '', new ext_goto(1, 'confmenu'));
1534
1535       $exten = 'confmenu';
1536       if (version_compare($version, "1.4", "ge")) {
1537         $ext->add($context, $exten, '', new ext_background('to-listen-to-it&press-1&to-rerecord-it&press-star,m,${CHANNEL(language)},macro-systemrecording'));
1538       } else {
1539         $ext->add($context, $exten, '', new ext_background('to-listen-to-it&press-1&to-rerecord-it&press-star,m,${LANGUAGE},macro-systemrecording'));
1540       }
1541       $ext->add($context, $exten, '', new ext_read('RECRESULT', '', 1, '', '', 4));
1542       $ext->add($context, $exten, '', new ext_gotoif('$["x${RECRESULT}"="x*"]', 'dorecord,1'));
1543       $ext->add($context, $exten, '', new ext_gotoif('$["x${RECRESULT}"="x1"]', 'docheck,1'));
1544       $ext->add($context, $exten, '', new ext_goto(1));
1545      
1546       $ext->add($context, '1', '', new ext_goto(1, 'docheck'));
1547       $ext->add($context, '*', '', new ext_goto(1, 'dorecord'));
1548      
1549       $ext->add($context, 't', '', new ext_playback('goodbye'));
1550       $ext->add($context, 't', '', new ext_hangup());
1551      
1552       $ext->add($context, 'i', '', new ext_playback('pm-invalid-option'));
1553       $ext->add($context, 'i', '', new ext_goto(1, 'confmenu'));
1554
1555       $ext->add($context, 'h', '', new ext_hangup());
1556      
1557     break;
1558   }
1559 }
1560
1561 /* begin page.ampusers.php functions */
1562
1563 function core_ampusers_add($username, $password, $extension_low, $extension_high, $deptname, $sections) {
1564   $sql = "INSERT INTO ampusers (username, password, extension_low, extension_high, deptname, sections) VALUES (";
1565   $sql .= "'".$username."',";
1566   $sql .= "'".$password."',";
1567   $sql .= "'".$extension_low."',";
1568   $sql .= "'".$extension_high."',";
1569   $sql .= "'".$deptname."',";
1570   $sql .= "'".implode(";",$sections)."');";
1571  
1572   sql($sql,"query");
1573 }
1574
1575 function core_ampusers_del($username) {
1576   $sql = "DELETE FROM ampusers WHERE username = '".$username."'";
1577   sql($sql,"query");
1578 }
1579
1580 function core_ampusers_list() {
1581   $sql = "SELECT username FROM ampusers ORDER BY username";
1582   return sql($sql,"getAll");
1583 }
1584
1585 /* end page.ampusers.php functions */
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595 /* begin page.did.php functions */
1596
1597 function core_did_list($order='extension'){
1598   switch ($order) {
1599     case 'description':
1600       $sql = "SELECT * FROM incoming ORDER BY description,extension,cidnum";
1601       break;
1602     case 'extension':
1603     default:
1604       $sql = "SELECT * FROM incoming ORDER BY extension,cidnum";
1605   }
1606   return sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1607 }
1608
1609 function core_did_get($extension="",$cidnum=""){
1610   global $db;
1611   $cidnum = $db->escapeSimple($cidnum);
1612   $sql = "SELECT * FROM incoming WHERE cidnum = \"$cidnum\" AND extension = \"$extension\"";
1613   return sql($sql,"getRow",DB_FETCHMODE_ASSOC);
1614 }
1615
1616 function core_did_del($extension,$cidnum){
1617   global $db;
1618   $cidnum = $db->escapeSimple($cidnum);
1619   $sql="DELETE FROM incoming WHERE cidnum = \"$cidnum\" AND extension = \"$extension\"";
1620   sql($sql);
1621 }
1622
1623 function core_did_edit($old_extension,$old_cidnum, $incoming){
1624
1625   $old_extension = addslashes(trim($old_extension));
1626   $old_cidnum = addslashes(trim($old_cidnum));
1627
1628   $incoming['extension'] = trim($incoming['extension']);
1629   $incoming['cidnum'] = trim($incoming['cidnum']);
1630
1631   $extension = addslashes($incoming['extension']);
1632   $cidnum = addslashes($incoming['cidnum']);
1633
1634   // if did or cid changed, then check to make sure that this pair is not already being used.
1635   //
1636   if (($extension != $old_extension) || ($cidnum != $old_cidnum)) {
1637     $existing=core_did_get($extension,$cidnum);
1638     if (empty($existing) && (trim($cidnum) == "")) {
1639       $existing_directdid = core_users_directdid_get($extension);
1640     } else {
1641       $existing_directdid = "";
1642     }
1643   } else {
1644     $existing = $existing_directdid = "";
1645   }
1646
1647   if (empty($existing) && empty($existing_directdid)) {
1648     core_did_del($old_extension,$old_cidnum);
1649     core_did_add($incoming);
1650     return true;
1651   } else {
1652     if (!empty($existing)) {
1653       echo "<script>javascript:alert('"._("A route for this DID/CID already exists!")." => ".$existing['extension']."/".$existing['cidnum']."')</script>";
1654     } else {
1655       echo "<script>javascript:alert('"._("A directdid for this DID is already associated with extension:")." ".$existing_directdid['extension']." (".$existing_directdid['name'].")')</script>";
1656     }
1657     return false;
1658   }
1659 }
1660
1661 function core_did_add($incoming){
1662   foreach ($incoming as $key => $val) { ${$key} = addslashes($val); } // create variables from request
1663
1664   // Check to make sure the did is not being used elsewhere
1665   //
1666   $existing=core_did_get($extension,$cidnum);
1667   if (empty($existing) && (trim($cidnum) == "")) {
1668     $existing_directdid = core_users_directdid_get($extension);
1669   } else {
1670     $existing_directdid = "";
1671   }
1672
1673   if (empty($existing) && empty($existing_directdid)) {
1674     $destination=${$goto0.'0'};
1675     $sql="INSERT INTO incoming (cidnum,extension,destination,faxexten,faxemail,answer,wait,privacyman,alertinfo, ringing, mohclass, description, grppre) values ('$cidnum','$extension','$destination','$faxexten','$faxemail','$answer','$wait','$privacyman','$alertinfo', '$ringing', '$mohclass', '$description', '$grppre')";
1676     sql($sql);
1677     return true;
1678   } else {
1679     if (!empty($existing)) {
1680       echo "<script>javascript:alert('"._("A route for this DID/CID already exists!")." => ".$existing['extension']."/".$existing['cidnum']."')</script>";
1681     } else {
1682       echo "<script>javascript:alert('"._("A directdid for this DID is already associated with extension:")." ".$existing_directdid['extension']." (".$existing_directdid['name'].")')</script>";
1683     }
1684     return false;
1685   }
1686 }
1687
1688 /* end page.did.php functions */
1689
1690
1691
1692
1693
1694
1695
1696 /* begin page.devices.php functions */
1697
1698 //get the existing devices
1699 function core_devices_list($tech="all") {
1700   $sql = "SELECT id,description FROM devices";
1701   switch (strtoupper($tech)) {
1702     case "IAX":
1703       $sql .= " WHERE tech = 'iax2'";
1704       break;
1705     case "IAX2":
1706     case "SIP":
1707     case "ZAP":
1708       $sql .= " WHERE tech = '".strtolower($tech)."'";
1709       break;
1710     case "ALL":
1711     default:
1712   }
1713   $sql .= ' ORDER BY id';
1714   $results = sql($sql,"getAll");
1715
1716   foreach($results as $result){
1717     if (checkRange($result[0])){
1718       $extens[] = array(
1719         0=>$result[0],  // for backwards compatibility
1720         1=>$result[1],
1721         'id'=>$result[0], // FETCHMODE_ASSOC emulation
1722         'description'=>$result[1],
1723       );
1724     }
1725   }
1726   if (isset($extens)) {
1727     return $extens;
1728   } else {
1729     return null;
1730   }
1731 }
1732
1733
1734 function core_devices_add($id,$tech,$dial,$devicetype,$user,$description,$emergency_cid=null,$editmode=false){
1735   global $amp_conf;
1736   global $currentFile;
1737   global $astman;
1738
1739   $display = isset($_REQUEST['display'])?$_REQUEST['display']:'';
1740
1741   if (trim($id) == '' ) {
1742     if ($display != 'extensions') {
1743       echo "<script>javascript:alert('"._("You must put in a device id")."');</script>";
1744     }
1745     return false;
1746   }
1747  
1748   //ensure this id is not already in use
1749   $devices = core_devices_list();
1750   if (is_array($devices)) {
1751     foreach($devices as $device) {
1752       if ($device[0] === $id) {
1753         if ($display <> 'extensions') echo "<script>javascript:alert('"._("This device id is already in use")."');</script>";
1754         return false;
1755       }
1756     }
1757   }
1758   //unless defined, $dial is TECH/id
1759   if ( $dial == '' ) {
1760     //zap is an exception
1761     if ( strtolower($tech) == "zap" ) {
1762       $zapchan = $_REQUEST['devinfo_channel'] != '' ? $_REQUEST['devinfo_channel'] : $_REQUEST['channel'];
1763       $dial = 'ZAP/'.$zapchan;
1764     } else {
1765       $dial = strtoupper($tech)."/".$id;
1766     }
1767   }
1768  
1769   //check to see if we are requesting a new user
1770   if ($user == "new") {
1771     $user = $id;
1772     $jump = true;
1773   }
1774  
1775   if(!get_magic_quotes_gpc()) {
1776     if(!empty($emergency_cid))
1777       $emergency_cid = addslashes($emergency_cid);
1778     if(!empty($description))
1779       $description = addslashes($description);
1780   }
1781  
1782   //insert into devices table
1783   $sql="INSERT INTO devices (id,tech,dial,devicetype,user,description,emergency_cid) values (\"$id\",\"$tech\",\"$dial\",\"$devicetype\",\"$user\",\"$description\",\"$emergency_cid\")";
1784   sql($sql);
1785  
1786   //add details to astdb
1787   if ($astman) {
1788     // if adding or editting a fixed device, user property should always be set
1789     if ($devicetype == 'fixed' || !$editmode) {
1790       $astman->database_put("DEVICE",$id."/user",$user);
1791     }
1792     // If changing from a fixed to an adhoc, the user property should be intialized
1793     // to the new default, not remain as the previous fixed user
1794     if ($editmode) {
1795       $previous_type = $astman->database_get("DEVICE",$id."/type");
1796       if ($previous_type == 'fixed' && $devicetype == 'adhoc') {
1797         $astman->database_put("DEVICE",$id."/user",$user);
1798       }
1799     }
1800     $astman->database_put("DEVICE",$id."/dial",$dial);
1801     $astman->database_put("DEVICE",$id."/type",$devicetype);
1802     $astman->database_put("DEVICE",$id."/default_user",$user);
1803     if($emergency_cid != '') {
1804       $astman->database_put("DEVICE",$id."/emergency_cid","\"".$emergency_cid."\"");
1805     }
1806
1807     if ($user != "none") {
1808       $existingdevices = $astman->database_get("AMPUSER",$user."/device");
1809       if (empty($existingdevices)) {
1810         $astman->database_put("AMPUSER",$user."/device",$id);
1811       } else {
1812         $existingdevices_array = explode('&',$existingdevices);
1813         if (!in_array($id, $existingdevices_array)) {
1814           $existingdevices_array[]=$id;
1815           $existingdevices = implode('&',$existingdevices_array);
1816           $astman->database_put("AMPUSER",$user."/device",$existingdevices);
1817         }
1818       }
1819     }
1820
1821   } else {
1822     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
1823   }
1824  
1825   // create a voicemail symlink if needed
1826   $thisUser = core_users_get($user);
1827   if(isset($thisUser['voicemail']) && ($thisUser['voicemail'] != "novm")) {
1828     if(empty($thisUser['voicemail'])) {
1829       $vmcontext = "default";
1830     } else {
1831       $vmcontext = $thisUser['voicemail'];
1832     }
1833    
1834     //voicemail symlink
1835     exec("rm -f /var/spool/asterisk/voicemail/device/".$id);
1836     exec("/bin/ln -s /var/spool/asterisk/voicemail/".$vmcontext."/".$user."/ /var/spool/asterisk/voicemail/device/".$id);
1837   }
1838    
1839   //take care of sip/iax/zap config
1840   $funct = "core_devices_add".strtolower($tech);
1841   if(function_exists($funct)){
1842     $funct($id);
1843   }
1844  
1845 /*  if($user != "none") {
1846     core_hint_add($user);
1847   }*/
1848  
1849   //if we are requesting a new user, let's jump to users.php
1850   if (isset($jump)) {
1851     echo("<script language=\"JavaScript\">window.location=\"config.php?display=users&extdisplay={$id}&name={$description}\";</script>");
1852   }
1853   return true;
1854 }
1855
1856 function core_devices_del($account,$editmode=false){
1857   global $amp_conf;
1858   global $currentFile;
1859   global $astman;
1860  
1861   //get all info about device
1862   $devinfo = core_devices_get($account);
1863  
1864   //delete details to astdb
1865   if ($astman) {
1866     // If a user was selected, remove this device from the user
1867     $deviceuser = $astman->database_get("DEVICE",$account."/user");
1868     if (isset($deviceuser) && $deviceuser != "none") {
1869       // Remove the device record from the user's device list
1870       $userdevices = $astman->database_get("AMPUSER",$deviceuser."/device");
1871
1872       // We need to remove just this user and leave the rest alone
1873       $userdevicesarr = explode("&", $userdevices);
1874       $userdevicesarr_hash = array_flip($userdevicesarr);
1875       unset($userdevicesarr_hash[$account]);
1876       $userdevicesarr = array_flip($userdevicesarr_hash);
1877       $userdevices = implode("&", $userdevicesarr);
1878      
1879       if (empty($userdevices)) {
1880           $astman->database_del("AMPUSER",$deviceuser."/device");
1881       } else {
1882           $astman->database_put("AMPUSER",$deviceuser."/device",$userdevices);
1883       }
1884     }
1885     if (! $editmode) {
1886       $astman->database_del("DEVICE",$account."/dial");
1887       $astman->database_del("DEVICE",$account."/type");
1888       $astman->database_del("DEVICE",$account."/user");
1889       $astman->database_del("DEVICE",$account."/default_user");
1890       $astman->database_del("DEVICE",$account."/emergency_cid");
1891     }
1892
1893     //delete from devices table
1894     $sql="DELETE FROM devices WHERE id = \"$account\"";
1895     sql($sql);
1896
1897     //voicemail symlink
1898     exec("rm -f /var/spool/asterisk/voicemail/device/".$account);
1899   } else {
1900     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
1901   }
1902  
1903   //take care of sip/iax/zap config
1904   $funct = "core_devices_del".strtolower($devinfo['tech']);
1905   if(function_exists($funct)){
1906     $funct($account);
1907   }
1908 }
1909
1910 function core_devices_get($account){
1911   //get all the variables for the meetme
1912   $sql = "SELECT * FROM devices WHERE id = '$account'";
1913   $results = sql($sql,"getRow",DB_FETCHMODE_ASSOC);
1914  
1915   //take care of sip/iax/zap config
1916   $funct = "core_devices_get".strtolower($results['tech']);
1917   if (!empty($results['tech']) && function_exists($funct)) {
1918     $devtech = $funct($account);
1919     if (is_array($devtech)){
1920       $results = array_merge($results,$devtech);
1921     }
1922   }
1923  
1924   return $results;
1925 }
1926
1927 // this function rebuilds the astdb based on device table contents
1928 // used on devices.php if action=resetall
1929 function core_devices2astdb(){
1930   global $astman;
1931   global $amp_conf;
1932
1933   $sql = "SELECT * FROM devices";
1934   $devresults = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1935
1936   //add details to astdb
1937   if ($astman) {
1938     $astman->database_deltree("DEVICE");
1939     foreach ($devresults as $dev) {
1940       extract($dev); 
1941       $astman->database_put("DEVICE",$id."/dial",$dial);
1942       $astman->database_put("DEVICE",$id."/type",$devicetype);
1943       $astman->database_put("DEVICE",$id."/user",$user);   
1944       $astman->database_put("DEVICE",$id."/default_user",$user);
1945       if(trim($emergency_cid) != '') {
1946         $astman->database_put("DEVICE",$id."/emergency_cid","\"".$emergency_cid."\"");
1947       }
1948       // If a user is selected, add this device to the user
1949       if ($user != "none") {
1950           $existingdevices = $astman->database_get("AMPUSER",$user."/device");
1951           if (!empty($existingdevices)) {
1952               $existingdevices .= "&";
1953           }
1954           $astman->database_put("AMPUSER",$user."/device",$existingdevices.$id);
1955       }
1956      
1957       // create a voicemail symlink if needed
1958       $thisUser = core_users_get($user);
1959       if(isset($thisUser['voicemail']) && ($thisUser['voicemail'] != "novm")) {
1960         if(empty($thisUser['voicemail']))
1961           $vmcontext = "default";
1962         else
1963           $vmcontext = $thisUser['voicemail'];
1964         //voicemail symlink
1965         exec("rm -f /var/spool/asterisk/voicemail/device/".$id);
1966         exec("/bin/ln -s /var/spool/asterisk/voicemail/".$vmcontext."/".$user."/ /var/spool/asterisk/voicemail/device/".$id);
1967       }
1968     }
1969     return true;
1970   } else {
1971     return false;
1972   }
1973 }
1974
1975 // this function rebuilds the astdb based on users table contents
1976 // used on devices.php if action=resetall
1977 function core_users2astdb(){
1978   global $amp_conf;
1979   global $astman;
1980
1981   $sql = "SELECT * FROM users";
1982   $userresults = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
1983  
1984   //add details to astdb
1985   if ($astman) {
1986     foreach($userresults as $usr) {
1987       extract($usr);
1988       $astman->database_put("AMPUSER",$extension."/password",$password);
1989       $astman->database_put("AMPUSER",$extension."/ringtimer",$ringtimer);
1990       $astman->database_put("AMPUSER",$extension."/noanswer",$noanswer);
1991       $astman->database_put("AMPUSER",$extension."/recording",$recording);
1992       $astman->database_put("AMPUSER",$extension."/outboundcid","\"".addslashes($outboundcid)."\"");
1993       $astman->database_put("AMPUSER",$extension."/cidname","\"".addslashes($name)."\"");
1994       $astman->database_put("AMPUSER",$extension."/voicemail","\"".$voicemail."\"");
1995     }
1996     return true;
1997   } else {
1998     return false;
1999   }
2000
2001 //  TODO: this was...
2002 //  return $astman->disconnect();
2003 //  is "true" the correct value...?
2004 }
2005
2006 //add to sip table
2007 function core_devices_addsip($account) {
2008   global $db;
2009   global $currentFile;
2010
2011   foreach ($_REQUEST as $req=>$data) {
2012     if ( substr($req, 0, 8) == 'devinfo_' ) {
2013       $keyword = substr($req, 8);
2014       if ( $keyword == 'dial' && $data == '' ) {
2015         $sipfields[] = array($account, $keyword, 'SIP/'.$account);
2016       } elseif ($keyword == 'mailbox' && $data == '') {
2017         $sipfields[] = array($account,'mailbox',$account.'@device');
2018       } else {
2019         $sipfields[] = array($account, $keyword, $data);
2020       }
2021     }
2022   }
2023  
2024   if ( !is_array($sipfields) ) { // left for compatibilty....lord knows why !
2025     $sipfields = array(
2026       //array($account,'account',$account),
2027       array($account,'accountcode',(isset($_REQUEST['accountcode']))?$_REQUEST['accountcode']:''),
2028       array($account,'secret',(isset($_REQUEST['secret']))?$_REQUEST['secret']:''),
2029       array($account,'canreinvite',(isset($_REQUEST['canreinvite']))?$_REQUEST['canreinvite']:'no'),
2030       array($account,'context',(isset($_REQUEST['context']))?$_REQUEST['context']:'from-internal'),
2031       array($account,'dtmfmode',(isset($_REQUEST['dtmfmode']))?$_REQUEST['dtmfmode']:''),
2032       array($account,'host',(isset($_REQUEST['host']))?$_REQUEST['host']:'dynamic'),
2033       array($account,'type',(isset($_REQUEST['type']))?$_REQUEST['type']:'friend'),
2034       array($account,'mailbox',(isset($_REQUEST['mailbox']) && !empty($_REQUEST['mailbox']))?$_REQUEST['mailbox']:$account.'@device'),
2035       array($account,'username',(isset($_REQUEST['username']))?$_REQUEST['username']:$account),
2036       array($account,'nat',(isset($_REQUEST['nat']))?$_REQUEST['nat']:'yes'),
2037       array($account,'port',(isset($_REQUEST['port']))?$_REQUEST['port']:'5060'),
2038       array($account,'qualify',(isset($_REQUEST['qualify']))?$_REQUEST['qualify']:'yes'),
2039       array($account,'callgroup',(isset($_REQUEST['callgroup']))?$_REQUEST['callgroup']:''),
2040       array($account,'pickupgroup',(isset($_REQUEST['pickupgroup']))?$_REQUEST['pickupgroup']:''),
2041       array($account,'disallow',(isset($_REQUEST['disallow']))?$_REQUEST['disallow']:''),
2042       array($account,'allow',(isset($_REQUEST['allow']))?$_REQUEST['allow']:'')
2043       //array($account,'record_in',(isset($_REQUEST['record_in']))?$_REQUEST['record_in']:'On-Demand'),
2044       //array($account,'record_out',(isset($_REQUEST['record_out']))?$_REQUEST['record_out']:'On-Demand'),
2045       //array($account,'callerid',(isset($_REQUEST['description']))?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>')
2046     );
2047   }
2048
2049   // Very bad
2050   $sipfields[] = array($account,'account',$account); 
2051   $sipfields[] = array($account,'callerid',(isset($_REQUEST['description']) && $_REQUEST['description'])?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>');
2052  
2053   // Where is this in the interface ??????
2054   $sipfields[] = array($account,'record_in',($_REQUEST['record_in'])?$_REQUEST['record_in']:'On-Demand');
2055   $sipfields[] = array($account,'record_out',($_REQUEST['record_out'])?$_REQUEST['record_out']:'On-Demand');
2056
2057   $compiled = $db->prepare('INSERT INTO sip (id, keyword, data) values (?,?,?)');
2058   $result = $db->executeMultiple($compiled,$sipfields);
2059   if(DB::IsError($result)) {
2060     die_freepbx($result->getDebugInfo()."<br><br>".'error adding to SIP table'); 
2061   }
2062 }
2063
2064 function core_devices_delsip($account) {
2065   global $db;
2066   global $currentFile;
2067  
2068   $sql = "DELETE FROM sip WHERE id = '$account'";
2069   $result = $db->query($sql);
2070  
2071   if(DB::IsError($result)) {
2072     die_freepbx($result->getMessage().$sql);
2073   }
2074 }
2075
2076 function core_devices_getsip($account) {
2077   global $db;
2078   $sql = "SELECT keyword,data FROM sip WHERE id = '$account'";
2079   $results = $db->getAssoc($sql);
2080   if(DB::IsError($results)) {
2081     $results = null;
2082   }
2083  
2084   return $results;
2085 }
2086
2087 //add to iax table
2088 function core_devices_addiax2($account) {
2089   global $db;
2090   global $currentFile;
2091  
2092   foreach ($_REQUEST as $req=>$data) {
2093     if ( substr($req, 0, 8) == 'devinfo_' ) {
2094       $keyword = substr($req, 8);
2095       if ( $keyword == 'dial' && $data == '' ) {
2096         $iaxfields[] = array($account, $keyword, 'IAX2/'.$account);
2097       } elseif ($keyword == 'mailbox' && $data == '') {
2098         $iaxfields[] = array($account,'mailbox',$account.'@device');
2099       } else {
2100         $iaxfields[] = array($account, $keyword, $data);
2101       }
2102     }
2103   }
2104  
2105   if ( !is_array($iaxfields) ) { // left for compatibilty....lord knows why !
2106     $iaxfields = array(
2107       //array($account,'account',$account),
2108       array($account,'secret',($_REQUEST['secret'])?$_REQUEST['secret']:''),
2109       array($account,'notransfer',($_REQUEST['notransfer'])?$_REQUEST['notransfer']:'yes'),
2110       array($account,'context',($_REQUEST['context'])?$_REQUEST['context']:'from-internal'),
2111       array($account,'host',($_REQUEST['host'])?$_REQUEST['host']:'dynamic'),
2112       array($account,'type',($_REQUEST['type'])?$_REQUEST['type']:'friend'),
2113       array($account,'mailbox',($_REQUEST['mailbox'])?$_REQUEST['mailbox']:$account.'@device'),
2114       array($account,'username',($_REQUEST['username'])?$_REQUEST['username']:$account),
2115       array($account,'port',($_REQUEST['port'])?$_REQUEST['port']:'4569'),
2116       array($account,'qualify',($_REQUEST['qualify'])?$_REQUEST['qualify']:'yes'),
2117       array($account,'disallow',($_REQUEST['disallow'])?$_REQUEST['disallow']:''),
2118       array($account,'allow',($_REQUEST['allow'])?$_REQUEST['allow']:''),
2119       array($account,'accountcode',($_REQUEST['accountcode'])?$_REQUEST['accountcode']:'')
2120       //array($account,'record_in',($_REQUEST['record_in'])?$_REQUEST['record_in']:'On-Demand'),
2121       //array($account,'record_out',($_REQUEST['record_out'])?$_REQUEST['record_out']:'On-Demand'),
2122       //array($account,'callerid',($_REQUEST['description'])?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>')
2123     );
2124   }
2125
2126   // Very bad
2127   $iaxfields[] = array($account,'account',$account); 
2128   $iaxfields[] = array($account,'callerid',(isset($_REQUEST['description']) && $_REQUEST['description'] != '')?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>');
2129   // Asterisk treats no caller ID from an IAX device as 'hide callerid', and ignores the caller ID
2130   // set in iax.conf. As we rely on this for pretty much everything, we need to specify the
2131   // callerid as a variable which gets picked up in macro-callerid.
2132   // Ref - http://bugs.digium.com/view.php?id=456
2133   $iaxfields[] = array($account,'setvar',"REALCALLERIDNUM=$account");
2134  
2135   // Where is this in the interface ??????
2136   $iaxfields[] = array($account,'record_in',($_REQUEST['record_in'])?$_REQUEST['record_in']:'On-Demand');
2137   $iaxfields[] = array($account,'record_out',($_REQUEST['record_out'])?$_REQUEST['record_out']:'On-Demand');
2138  
2139   $compiled = $db->prepare('INSERT INTO iax (id, keyword, data) values (?,?,?)');
2140   $result = $db->executeMultiple($compiled,$iaxfields);
2141   if(DB::IsError($result)) {
2142     die_freepbx($result->getMessage()."<br><br>error adding to IAX table");
2143   }
2144 }
2145
2146 function core_devices_deliax2($account) {
2147   global $db;
2148   global $currentFile;
2149  
2150   $sql = "DELETE FROM iax WHERE id = '$account'";
2151   $result = $db->query($sql);
2152  
2153   if(DB::IsError($result)) {
2154     die_freepbx($result->getMessage().$sql);
2155   }
2156 }
2157
2158 function core_devices_getiax2($account) {
2159   global $db;
2160   $sql = "SELECT keyword,data FROM iax WHERE id = '$account'";
2161   $results = $db->getAssoc($sql);
2162   if(DB::IsError($results)) {
2163     $results = null;
2164   }
2165  
2166   return $results;
2167 }
2168
2169 function core_devices_addzap($account) {
2170   global $db;
2171   global $currentFile;
2172  
2173   foreach ($_REQUEST as $req=>$data) {
2174     if ( substr($req, 0, 8) == 'devinfo_' ) {
2175       $keyword = substr($req, 8);
2176       if ( $keyword == 'dial' && $data == '' ) {
2177         $zapchan = $_REQUEST['devinfo_channel'] != '' ? $_REQUEST['devinfo_channel'] : $_REQUEST['channel'];
2178         $zapfields[] = array($account, $keyword, 'ZAP/'.$zapchan);
2179       } elseif ($keyword == 'mailbox' && $data == '') {
2180         $zapfields[] = array($account,'mailbox',$account.'@device');
2181       } else {
2182         $zapfields[] = array($account, $keyword, $data);
2183       }
2184     }
2185   }
2186  
2187   if ( !is_array($zapfields) ) { // left for compatibilty....lord knows why !
2188     $zapfields = array(
2189       //array($account,'account',$account),
2190       array($account,'context',($_REQUEST['context'])?$_REQUEST['context']:'from-internal'),
2191       array($account,'mailbox',($_REQUEST['mailbox'])?$_REQUEST['mailbox']:$account.'@device'),
2192       //array($account,'callerid',($_REQUEST['description'])?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>'),
2193       array($account,'immediate',($_REQUEST['immediate'])?$_REQUEST['immediate']:'no'),
2194       array($account,'signalling',($_REQUEST['signalling'])?$_REQUEST['signalling']:'fxo_ks'),
2195       array($account,'echocancel',($_REQUEST['echocancel'])?$_REQUEST['echocancel']:'yes'),
2196       array($account,'echocancelwhenbridged',($_REQUEST['echocancelwhenbridged'])?$_REQUEST['echocancelwhenbridged']:'no'),
2197       array($account,'immediate',($_REQUEST['immediate'])?$_REQUEST['immediate']:'no'),
2198       array($account,'echotraining',($_REQUEST['echotraining'])?$_REQUEST['echotraining']:'800'),
2199       array($account,'busydetect',($_REQUEST['busydetect'])?$_REQUEST['busydetect']:'no'),
2200       array($account,'busycount',($_REQUEST['busycount'])?$_REQUEST['busycount']:'7'),
2201       array($account,'callprogress',($_REQUEST['callprogress'])?$_REQUEST['callprogress']:'no'),
2202       //array($account,'record_in',($_REQUEST['record_in'])?$_REQUEST['record_in']:'On-Demand'), 
2203       //array($account,'record_out',($_REQUEST['record_out'])?$_REQUEST['record_out']:'On-Demand'),
2204       array($account,'accountcode',(isset($_REQUEST['accountcode']))?$_REQUEST['accountcode']:''),
2205       array($account,'channel',($_REQUEST['channel'])?$_REQUEST['channel']:'')
2206     );
2207   }
2208
2209   // Very bad
2210   $zapfields[] = array($account,'account',$account); 
2211   $zapfields[] = array($account,'callerid',($_REQUEST['description'])?$_REQUEST['description']." <".$account.'>':'device'." <".$account.'>');
2212  
2213   // Where is this in the interface ??????
2214   $zapfields[] = array($account,'record_in',($_REQUEST['record_in'])?$_REQUEST['record_in']:'On-Demand');
2215   $zapfields[] = array($account,'record_out',($_REQUEST['record_out'])?$_REQUEST['record_out']:'On-Demand');
2216
2217   $compiled = $db->prepare('INSERT INTO zap (id, keyword, data) values (?,?,?)');
2218   $result = $db->executeMultiple($compiled,$zapfields);
2219   if(DB::IsError($result)) {
2220     die_freepbx($result->getMessage()."<br><br>error adding to ZAP table");
2221   }
2222 }
2223
2224 function core_devices_delzap($account) {
2225   global $db;
2226   global $currentFile;
2227  
2228   $sql = "DELETE FROM zap WHERE id = '$account'";
2229   $result = $db->query($sql);
2230   if(DB::IsError($result)) {
2231     die_freepbx($result->getMessage().$sql);
2232   }
2233 }
2234
2235 function core_devices_getzap($account) {
2236   global $db;
2237   $sql = "SELECT keyword,data FROM zap WHERE id = '$account'";
2238   $results = $db->getAssoc($sql);
2239   if(DB::IsError($results)) {
2240     $results = null;
2241   }
2242   return $results;
2243 }
2244 /* end page.devices.php functions */
2245
2246
2247
2248
2249 function core_hint_get($account){
2250   global $astman;
2251
2252   // We should always check the AMPUSER in case they logged into a device
2253   // but we will fall back to the old methond if $astman not open although
2254   // I'm pretty sure everything else will puke anyhow if not running
2255   //
2256   if ($astman) {
2257     $device=$astman->database_get("AMPUSER",$account."/device");
2258     $device_arr = explode('&',$device);
2259     $sql = "SELECT dial from devices where id in ('".implode("','",$device_arr)."')";
2260   } else {
2261     $sql = "SELECT dial from devices where user = '{$account}'";
2262   }
2263   $results = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
2264  
2265   //create an array of strings
2266   if (is_array($results)){
2267     foreach ($results as $result) {
2268       $dial[] = $result['dial'];
2269     }
2270   }
2271  
2272   //create a string with & delimiter
2273   if (isset($dial) && is_array($dial)){
2274     $hint = implode($dial,"&");
2275   } else {
2276     if (isset($results[0]['dial'])) {
2277       $hint = $results[0]['dial'];
2278     } else {
2279       $hint = null;
2280     }
2281   }
2282  
2283   return $hint;
2284 }
2285
2286
2287
2288 /* begin page.users.php functions */
2289
2290 // get the existing extensions
2291 // the returned arrays contain [0]:extension [1]:name
2292 function core_users_list() {
2293   $results = sql("SELECT extension,name,voicemail FROM users ORDER BY extension","getAll");
2294
2295   //only allow extensions that are within administrator's allowed range
2296   foreach($results as $result){
2297     if (checkRange($result[0])){
2298       $extens[] = array($result[0],$result[1],$result[2]);
2299     }
2300   }
2301  
2302   if (isset($extens)) {
2303     sort($extens);
2304     return $extens;
2305   } else {
2306     return null;
2307   }
2308 }
2309
2310 function core_check_extensions($exten=true) {
2311   global $amp_conf;
2312
2313   $extenlist = array();
2314   if (is_array($exten) && empty($exten)) {
2315     return $extenlist;
2316   }
2317   $sql = "SELECT extension, name FROM users ";
2318   if (is_array($exten)) {
2319     $sql .= "WHERE extension in ('".implode("','",$exten)."')";
2320   }
2321   $sql .= " ORDER BY extension";
2322   $results = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
2323
2324   foreach ($results as $result) {
2325     $thisexten = $result['extension'];
2326     $extenlist[$thisexten]['description'] = _("User Extension: ").$result['name'];
2327     $extenlist[$thisexten]['status'] = 'INUSE';
2328     $display = ($amp_conf['AMPEXTENSIONS'] == "deviceanduser")?'users':'extensions';
2329     $extenlist[$thisexten]['edit_url'] = "config.php?type=setup&display=$display&extdisplay=".urlencode($thisexten)."&skip=0";
2330   }
2331   return $extenlist;
2332 }
2333
2334 function core_check_destinations($dest=true) {
2335   global $active_modules;
2336
2337   $destlist = array();
2338   if (is_array($dest) && empty($dest)) {
2339     return $destlist;
2340   }
2341   $sql = "SELECT extension, cidnum, description, destination FROM incoming ";
2342   if ($dest !== true) {
2343     $sql .= "WHERE destination in ('".implode("','",$dest)."')";
2344   }
2345   $sql .= "ORDER BY extension, cidnum";
2346   $results = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
2347
2348   //$type = isset($active_modules['announcement']['type'])?$active_modules['announcement']['type']:'setup';
2349
2350   foreach ($results as $result) {
2351     $thisdest = $result['destination'];
2352     $thisid   = $result['extension'].'/'.$result['cidnum'];
2353     $destlist[] = array(
2354       'dest' => $thisdest,
2355       'description' => 'Inbound Route: '.$result['description'].' ('.$thisid.')',
2356       'edit_url' => 'config.php?display=did&extdisplay='.urlencode($thisid),
2357     );
2358   }
2359   return $destlist;
2360 }
2361
2362 function core_sipname_check($sipname, $extension) {
2363   global $db;
2364   if (!isset($sipname) || trim($sipname)=='')
2365     return true;
2366
2367   $sql = "SELECT sipname FROM users WHERE sipname = '$sipname' AND extension != '$extension'";
2368   $results = $db->getRow($sql,DB_FETCHMODE_ASSOC);
2369   if(DB::IsError($results)) {
2370         die_freepbx($results->getMessage().$sql);
2371   }
2372  
2373   if (isset($results['sipname']) && trim($results['sipname']) == $sipname)
2374     return false;
2375   else
2376     return true;
2377 }
2378
2379 function core_users_add($vars, $editmode=false) {
2380   extract($vars);
2381  
2382   global $db;
2383   global $amp_conf;
2384   global $astman;
2385
2386   $thisexten = isset($thisexten) ? $thisexten : '';
2387
2388   if (trim($extension) == '' ) {
2389     echo "<script>javascript:alert('"._("You must put in an extension (or user) number")."');</script>";
2390     return false;
2391   }
2392
2393   //ensure this id is not already in use
2394   $extens = core_users_list();
2395   if(is_array($extens)) {
2396     foreach($extens as $exten) {
2397       if ($exten[0]===$extension) {
2398         echo "<script>javascript:alert('".sprintf(_("This user/extension %s is already in use"),$extension)."');</script>";
2399         return false;
2400       }
2401     }
2402   }
2403
2404   // clean and check the did to make sure it is not being used by another extension or in did routing
2405   //
2406   $directdid = preg_replace("/[^0-9._XxNnZz\[\]\-\+]/" ,"", trim($directdid));
2407   if (trim($directdid) != "") {
2408     $existing=core_did_get($directdid,"");
2409     $existing_directdid = empty($existing)?core_users_directdid_get($directdid):$existing;
2410     if (!empty($existing) || !empty($existing_directdid)) {
2411       if (!empty($existing)) {
2412         echo "<script>javascript:alert('"._("A route with this DID already exists:")." ".$existing['extension']."')</script>";
2413       } else {
2414         echo "<script>javascript:alert('"._("This DID is already associated with extension:")." ".$existing_directdid['extension']." (".$existing_directdid['name'].")')</script>";
2415       }
2416       return false;
2417     }
2418   }
2419
2420   $sipname = preg_replace("/\s/" ,"", trim($sipname));
2421   if (! core_sipname_check($sipname, $extension)) {
2422     echo "<script>javascript:alert('"._("This sipname: {$sipname} is already in use")."');</script>";
2423     return false;
2424   }
2425  
2426   //build the recording variable
2427   $recording = "out=".$record_out."|in=".$record_in;
2428  
2429   //escape quotes and any other bad chars:
2430   if(!get_magic_quotes_gpc()) {
2431     $outboundcid = addslashes($outboundcid);
2432     $name = addslashes($name);
2433   }
2434
2435   //if voicemail is enabled, set the box@context to use
2436   //havn't checked but why is voicemail needed on users anyway?  Doesn't exactly make it modular !
2437   if ( function_exists('voicemail_mailbox_get') ) {
2438     $vmbox = voicemail_mailbox_get($extension);
2439     if ( $vmbox == null ) {
2440       $voicemail = "novm";
2441       $vmx_state = "false";
2442     } else {
2443       $voicemail = $vmbox['vmcontext'];
2444     }
2445   }
2446
2447   // MODIFICATION: (PL)
2448   // Added for directdid and didalert l for Alert Info distinctive ring)
2449   //
2450   // cleanup any non dial pattern characters prior to inserting into the database
2451   // then add directdid to the insert command.
2452   //
2453   // Clean replace any <> with () in display name - should have javascript stopping this but ...
2454   //
2455   $name = preg_replace(array('/</','/>/'), array('(',')'), trim($name));
2456  
2457   //insert into users table
2458   $sql="INSERT INTO users (extension,password,name,voicemail,ringtimer,noanswer,recording,outboundcid,directdid,didalert,faxexten,faxemail,answer,wait,privacyman,mohclass,sipname) values (\"";
2459   $sql.= "$extension\", \"";
2460   $sql.= isset($password)?$password:'';
2461   $sql.= "\", \"";
2462   $sql.= isset($name)?$name:'';
2463   $sql.= "\", \"";
2464   $sql.= isset($voicemail)?$voicemail:'default';
2465   $sql.= "\", \"";
2466   $sql.= isset($ringtimer)?$ringtimer:'';
2467   $sql.= "\", \"";
2468   $sql.= isset($noanswer)?$noanswer:'';
2469   $sql.= "\", \"";
2470   $sql.= isset($recording)?$recording:'';
2471   $sql.= "\", \"";
2472   $sql.= isset($outboundcid)?$outboundcid:'';
2473   $sql.= "\", \"";
2474   $sql.= isset($directdid)?$directdid:'';
2475   $sql.= "\", \"";
2476   $sql.= isset($didalert)?$didalert:'';
2477
2478   $sql.= "\", \"";
2479   $sql.= isset($faxexten)?$faxexten:'';
2480   $sql.= "\", \"";
2481   $sql.= isset($faxemail)?$faxemail:'';
2482   $sql.= "\", \"";
2483   $sql.= isset($answer)?$answer:'';
2484   $sql.= "\", \"";
2485   $sql.= isset($wait)?$wait:'';
2486   $sql.= "\", \"";
2487   $sql.= isset($privacyman)?$privacyman:'';
2488   $sql.= "\", \"";
2489   $sql.= isset($mohclass)?$mohclass:'';
2490   $sql.= "\", \"";
2491   $sql.= isset($sipname)?$sipname:'';
2492   $sql.= "\")";
2493   sql($sql);
2494
2495   //write to astdb
2496   if ($astman) {
2497     $cid_masquerade = (isset($cid_masquerade) && trim($cid_masquerade) != "")?trim($cid_masquerade):$extension;
2498     $astman->database_put("AMPUSER",$extension."/password",isset($password)?$password:'');
2499     $astman->database_put("AMPUSER",$extension."/ringtimer",isset($ringtimer)?$ringtimer:'');
2500     $astman->database_put("AMPUSER",$extension."/noanswer",isset($noanswer)?$noanswer:'');
2501     $astman->database_put("AMPUSER",$extension."/recording",isset($recording)?$recording:'');
2502     $astman->database_put("AMPUSER",$extension."/outboundcid",isset($outboundcid)?"\"".$outboundcid."\"":'');
2503     $astman->database_put("AMPUSER",$extension."/cidname",isset($name)?"\"".$name."\"":'');
2504     $astman->database_put("AMPUSER",$extension."/cidnum",$cid_masquerade);
2505     $astman->database_put("AMPUSER",$extension."/voicemail","\"".isset($voicemail)?$voicemail:''."\"");
2506     if (!$editmode) {
2507       $astman->database_put("AMPUSER",$extension."/device","\"".((isset($device))?$device:'')."\"");
2508     }
2509
2510     if (trim($callwaiting) == 'enabled') {
2511       $astman->database_put("CW",$extension,"\"ENABLED\"");
2512     } else if (trim($callwaiting) == 'disabled') {
2513       $astman->database_del("CW",$extension);
2514     } else {
2515       echo "ERROR: this state should not exist<br>";
2516     }
2517
2518     if ($vmx_state && $voicemail != "novm") {
2519
2520       $unavail_mode="enabled";
2521       $busy_mode="disabled";
2522       $vmx_state=$astman->database_get("AMPUSER",$extension."/vmx/unavail/state");
2523
2524       if (trim($vmx_state) == 'blocked') {
2525
2526         $astman->database_put("AMPUSER", "$extension/vmx/unavail/state", "$unavail_mode");
2527         $astman->database_put("AMPUSER", "$extension/vmx/busy/state", "$busy_mode");
2528
2529       } elseif (trim($vmx_state) != 'enabled' && trim($vmx_state) != 'disabled') {
2530
2531         $repeat="1";
2532         $timeout="2";
2533         $vmxopts_timeout="";
2534         $loops="1";
2535
2536         $mode="unavail";
2537         $astman->database_put("AMPUSER", "$extension/vmx/$mode/state", "$unavail_mode");
2538         $astman->database_put("AMPUSER", "$extension/vmx/$mode/vmxopts/timeout", "$vmxopts_timeout");
2539
2540         $mode="busy";
2541         $astman->database_put("AMPUSER", "$extension/vmx/$mode/state", "$busy_mode");
2542         $astman->database_put("AMPUSER", "$extension/vmx/$mode/vmxopts/timeout", "$vmxopts_timeout");
2543        
2544       }
2545     } else {
2546       $vmx_state=$astman->database_get("AMPUSER",$extension."/vmx/unavail/state");
2547       if (trim($vmx_state) == 'enabled' || trim($vmx_state) == 'disabled' || trim($vmx_state) == 'blocked') {
2548         $astman->database_put("AMPUSER", "$extension/vmx/unavail/state", "blocked");
2549         $astman->database_put("AMPUSER", "$extension/vmx/busy/state", "blocked");
2550       }
2551     }
2552   } else {
2553     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
2554   }
2555   return true;
2556 }
2557
2558 function core_users_get($extension){
2559   global $db;
2560   global $amp_conf;
2561   global $astman;
2562   //get all the variables for the meetme
2563   $sql = "SELECT * FROM users WHERE extension = '$extension'";
2564   $results = $db->getRow($sql,DB_FETCHMODE_ASSOC);
2565   if(DB::IsError($results)) {
2566     die_freepbx($results->getMessage().$sql);
2567   }
2568   if (empty($results)) {
2569     return $results;
2570   }
2571  
2572   //explode recording vars
2573   $recording = explode("|",$results['recording']);
2574   if (isset($recording[1])) {
2575     $recout = substr($recording[0],4);
2576     $recin = substr($recording[1],3);
2577     $results['record_in']=$recin;
2578     $results['record_out']=$recout;
2579   } else {
2580     $results['record_in']='Adhoc';
2581     $results['record_out']='Adhoc';
2582   }
2583   if ($astman) {
2584     $cw = $astman->database_get("CW",$extension);
2585     $results['callwaiting'] = (trim($cw) == 'ENABLED') ? 'enabled' : 'disabled';
2586     $results['vmx_state']=$astman->database_get("AMPUSER",$extension."/vmx/unavail/state");
2587     $cid_masquerade=$astman->database_get("AMPUSER",$extension."/cidnum");
2588     $results['cid_masquerade'] = (trim($cid_masquerade) != "")?$cid_masquerade:$extension;
2589   } else {
2590     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
2591   }
2592
2593   return $results;
2594 }
2595
2596 function core_users_del($extension, $editmode=false){
2597   global $db;
2598   global $amp_conf;
2599   global $astman;
2600  
2601   //delete from devices table
2602   $sql="DELETE FROM users WHERE extension = \"$extension\"";
2603   $results = $db->query($sql);
2604   if(DB::IsError($results)) {
2605     die_freepbx($results->getMessage().$sql);
2606   }
2607
2608   //delete details to astdb
2609   if ($astman && !$editmode) {
2610     $astman->database_del("AMPUSER",$extension."/password");
2611     $astman->database_del("AMPUSER",$extension."/ringtimer");
2612     $astman->database_del("AMPUSER",$extension."/noanswer");
2613     $astman->database_del("AMPUSER",$extension."/recording");
2614     $astman->database_del("AMPUSER",$extension."/outboundcid");
2615     $astman->database_del("AMPUSER",$extension."/cidname");
2616     $astman->database_del("AMPUSER",$extension."/cidnum");
2617     $astman->database_del("AMPUSER",$extension."/voicemail");
2618     $astman->database_del("AMPUSER",$extension."/device");
2619   }
2620 }
2621
2622 function core_users_directdid_get($directdid=""){
2623   if (empty($directdid)) {
2624     return array();
2625   } else {
2626     $sql = "SELECT * FROM users WHERE directdid = \"$directdid\"";
2627     return sql($sql,"getRow",DB_FETCHMODE_ASSOC);
2628   }
2629 }
2630
2631 function core_users_cleanastdb($extension) {
2632   // This is called to remove any ASTDB traces of the user after a deletion. Otherwise,
2633   // call forwarding, call waiting settings could hang around and bite someone if they
2634   // recycle an extension. Is called from page.xtns and page.users.
2635   global $amp_conf;
2636   global $astman;
2637
2638   if ($astman) {
2639     $astman->database_del("CW",$extension);
2640     $astman->database_del("CF",$extension);
2641     $astman->database_del("CFB",$extension);
2642     $astman->database_del("CFU",$extension);
2643     $astman->database_deltree("AMPUSER/".$extension."/vmx");
2644
2645   } else {
2646     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
2647   }
2648 }
2649
2650 function core_users_edit($extension,$vars){
2651   global $db;
2652   global $amp_conf;
2653   global $astman;
2654  
2655   //I we are editing, we need to remember existing user<->device mapping, so we can delete and re-add
2656   if ($astman) {
2657     $ud = $astman->database_get("AMPUSER",$extension."/device");
2658     $current_vmcontext = $astman->database_get("AMPUSER",$extension."/voicemail");
2659     $new_vmcontext = isset($vars['vmcontext']) ? $vars['vmcontext'] : 'novm';
2660     $vars['device'] = $ud;
2661   } else {
2662     fatal("Cannot connect to Asterisk Manager with ".$amp_conf["AMPMGRUSER"]."/".$amp_conf["AMPMGRPASS"]);
2663   }
2664  
2665   $directdid=$vars['directdid'];
2666   $directdid = preg_replace("/[^0-9._XxNnZz\[\]\-\+]/" ,"", trim($directdid));
2667   // clean and check the did to make sure it is not being used by another extension or in did routing
2668   //
2669   if (trim($directdid) != "") {
2670     $existing=core_did_get($directdid,"");
2671     $existing_directdid = empty($existing)?core_users_directdid_get($directdid):$existing;
2672     if (!empty($existing) || (!empty($existing_directdid) && $existing_directdid['extension'] != $extension)) {
2673       if (!empty($existing)) {
2674         echo "<script>javascript:alert('"._("A route with this DID already exists:")." ".$existing['extension']."')</script>";
2675       } else {
2676         echo "<script>javascript:alert('"._("This DID is already associated with extension:")." ".$existing_directdid['extension']." (".$existing_directdid['name'].")')</script>";
2677       }
2678       return false;
2679     }
2680   }
2681
2682   //delete and re-add
2683   if (core_sipname_check($vars['sipname'],$extension)) {
2684     core_users_del($extension, true);
2685     core_users_add($vars, true);
2686
2687     // If the vmcontext has changed, we need to change all the links. In extension mode, the link
2688     // to the current fixed device will get changed, but none others will
2689     //
2690     if ($current_vmcontext != $new_vmcontext) {
2691       $user_devices = explode('&',$ud);
2692       foreach ($user_devices as $user_device) {
2693         exec("rm -f /var/spool/asterisk/voicemail/device/".$user_device);
2694         if ($new_context != 'novm') {
2695           exec("/bin/ln -s /var/spool/asterisk/voicemail/".$new_vmcontext."/".$extension."/ /var/spool/asterisk/voicemail/device/".$user_device);
2696         }
2697       }
2698     }
2699   }
2700   return true;
2701 }
2702
2703 function core_directdid_list(){
2704   $sql = "SELECT extension, directdid, didalert, mohclass, faxexten, faxemail, answer, wait, privacyman FROM users WHERE directdid IS NOT NULL AND directdid != ''";
2705   return sql($sql,"getAll",DB_FETCHMODE_ASSOC);
2706 }
2707
2708
2709
2710 function core_zapchandids_add($description, $channel, $did) {
2711   global $db;
2712
2713
2714   if (!ctype_digit(trim($channel)) || trim($channel) == '') {
2715     echo "<script>javascript:alert('"._('Invalid Channel Number, must be numeric and not blank')."')</script>";
2716     return false;
2717   }
2718   if (trim($did) == '') {
2719     echo "<script>javascript:alert('"._('Invalid DID, must be a non-blank DID')."')</script>";
2720     return false;
2721   }
2722
2723   $description = q($description);
2724   $channel     = q($channel);
2725   $did         = q($did);
2726
2727   $sql = "INSERT INTO zapchandids (channel, description, did) VALUES ($channel, $description, $did)";
2728   $results = $db->query($sql);
2729   if (DB::IsError($results)) {
2730     if ($results->getCode() == DB_ERROR_ALREADY_EXISTS) {
2731       echo "<script>javascript:alert('"._("Error Duplicate Channel Entry")."')</script>";
2732       return false;
2733     } else {
2734       die_freepbx($results->getMessage()."<br><br>".$sql);
2735     }
2736   }
2737   return true;
2738 }
2739
2740 function core_zapchandids_edit($description, $channel, $did) {
2741   global $db;
2742
2743   $description = q($description);
2744   $channel     = q($channel);
2745   $did         = q($did);
2746
2747   $sql = "UPDATE zapchandids SET description = $description, did = $did WHERE channel = $channel";
2748   $results = $db->query($sql);
2749   if (DB::IsError($results)) {
2750     die_freepbx($results->getMessage()."<br><br>".$sql);
2751   }
2752   return true;
2753 }
2754
2755 function core_zapchandids_delete($channel) {
2756   global $db;
2757
2758   $channel     = q($channel);
2759
2760   $sql = "DELETE FROM zapchandids WHERE channel = $channel";
2761   $results = $db->query($sql);
2762   if (DB::IsError($results)) {
2763     die_freepbx($results->getMessage()."<br><br>".$sql);
2764   }
2765   return true;
2766 }
2767
2768 function core_zapchandids_list() {
2769   global $db;
2770
2771   $sql = "SELECT * FROM zapchandids ORDER BY channel";
2772   return sql($sql,"getAll",DB_FETCHMODE_ASSOC);
2773 }
2774
2775 function core_zapchandids_get($channel) {
2776   global $db;
2777
2778   $channel     = q($channel);
2779
2780   $sql = "SELECT * FROM zapchandids WHERE channel = $channel";
2781   return sql($sql,"getRow",DB_FETCHMODE_ASSOC);
2782 }
2783
2784 /* end page.users.php functions */
2785
2786
2787
2788
2789
2790 /* begin page.trunks.php functions */
2791
2792 // we're adding ,don't require a $trunknum
2793 function core_trunks_add($tech, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register, $keepcid, $failtrunk, $disabletrunk) {
2794   global $db;
2795  
2796   // find the next available ID
2797   $trunknum = 1;
2798
2799   // This is pretty ugle, will fix when we redo trunks and routes with proper uniqueids.
2800   // get the list, sort them, then look for a hole and use it, or overflow to the end if
2801   // not and use that
2802   //
2803   $trunk_hash = array();
2804   foreach(core_trunks_list() as $trunk) {
2805     $trunknum = ltrim($trunk[0],"OUT_");
2806     $trunk_hash[] = $trunknum;
2807   }
2808   sort($trunk_hash);
2809   $trunknum = 1;
2810   foreach ($trunk_hash as $trunk_id) {
2811     if ($trunk_id != $trunknum) {
2812       break;
2813     }
2814     $trunknum++;
2815   }
2816  
2817   core_trunks_backendAdd($trunknum, $tech, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register, $keepcid, $failtrunk, $disabletrunk);
2818  
2819   return $trunknum;
2820 }
2821
2822 function core_trunks_del($trunknum, $tech = null) {
2823   global $db;
2824  
2825   if ($tech === null) { // in EditTrunk, we get this info anyways
2826     $tech = core_trunks_getTrunkTech($trunknum);
2827   }
2828
2829   //delete from globals table
2830   sql("DELETE FROM globals WHERE variable LIKE '%OUT_$trunknum' OR variable IN ('OUTCID_$trunknum','OUTMAXCHANS_$trunknum','OUTPREFIX_$trunknum','OUTKEEPCID_$trunknum','OUTFAIL_$trunknum','OUTDISABLE_$trunknum')");
2831  
2832   //write outids
2833   core_trunks_writeoutids();
2834
2835   // conditionally, delete from iax or sip
2836   switch (strtolower($tech)) {
2837     case "iax":
2838     case "iax2":
2839       sql("DELETE FROM iax WHERE id = '9999$trunknum' OR id = '99999$trunknum' OR id = '9999999$trunknum'");
2840     break;
2841     case "sip":
2842       sql("DELETE FROM sip WHERE id = '9999$trunknum' OR id = '99999$trunknum' OR id = '9999999$trunknum'");
2843     break;
2844   }
2845 }
2846
2847 function core_trunks_edit($trunknum, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register, $keepcid, $failtrunk, $disabletrunk) {
2848   //echo "editTrunk($trunknum, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register)";
2849   $tech = core_trunks_getTrunkTech($trunknum);
2850   core_trunks_del($trunknum, $tech);
2851   core_trunks_backendAdd($trunknum, $tech, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register, $keepcid, $failtrunk, $disabletrunk);
2852 }
2853
2854 // just used internally by addTrunk() and editTrunk()
2855 //obsolete
2856 function core_trunks_backendAdd($trunknum, $tech, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register, $keepcid, $failtrunk, $disabletrunk) {
2857   global $db;
2858  
2859   if  (is_null($dialoutprefix)) $dialoutprefix = ""; // can't be NULL
2860  
2861   //echo  "backendAddTrunk($trunknum, $tech, $channelid, $dialoutprefix, $maxchans, $outcid, $peerdetails, $usercontext, $userconfig, $register)";
2862  
2863   // change iax to "iax2" (only spot we actually store iax2, since its used by Dial()..)
2864   $techtemp = ((strtolower($tech) == "iax") ? "iax2" : $tech);
2865   $outval = (($techtemp == "custom") ? "AMP:".$channelid : strtoupper($techtemp).'/'.$channelid);
2866  
2867   $glofields = array(
2868       array('OUT_'.$trunknum, $outval),
2869       array('OUTPREFIX_'.$trunknum, $dialoutprefix),
2870       array('OUTMAXCHANS_'.$trunknum, $maxchans),
2871       array('OUTCID_'.$trunknum, $outcid),
2872       array('OUTKEEPCID_'.$trunknum, $keepcid),
2873       array('OUTFAIL_'.$trunknum, $failtrunk),
2874       array('OUTDISABLE_'.$trunknum, $disabletrunk),
2875       );
2876      
2877   unset($techtemp);
2878  
2879   $compiled = $db->prepare('INSERT INTO globals (variable, value) values (?,?)');
2880   $result = $db->executeMultiple($compiled,$glofields);
2881   if(DB::IsError($result)) {
2882     die_freepbx($result->getMessage()."<br><br>".$sql);
2883   }
2884  
2885   core_trunks_writeoutids();
2886
2887   $disable_flag = ($disabletrunk == "on")?1:0;
2888  
2889   switch (strtolower($tech)) {
2890     case "iax":
2891     case "iax2":
2892       core_trunks_addSipOrIax($peerdetails,'iax',$channelid,$trunknum,$disable_flag);
2893       if ($usercontext != ""){
2894         core_trunks_addSipOrIax($userconfig,'iax',$usercontext,'9'.$trunknum,$disable_flag);
2895       }
2896       if ($register != ""){
2897         core_trunks_addRegister($trunknum,'iax',$register,$disable_flag);
2898       }
2899     break;
2900     case "sip":
2901       core_trunks_addSipOrIax($peerdetails,'sip',$channelid,$trunknum,$disable_flag);
2902       if ($usercontext != ""){
2903         core_trunks_addSipOrIax($userconfig,'sip',$usercontext,'9'.$trunknum,$disable_flag);
2904       }
2905       if ($register != ""){
2906         core_trunks_addRegister($trunknum,'sip',$register,$disable_flag);
2907       }
2908     break;
2909   }
2910 }
2911
2912 function core_trunks_getTrunkTech($trunknum) {
2913
2914   $results = sql("SELECT value FROM globals WHERE variable = 'OUT_".$trunknum."'","getAll");
2915   if (!$results) {
2916     return false;
2917   }
2918   if(strpos($results[0][0],"AMP:") === 0) {  //custom trunks begin with AMP:
2919     $tech = "custom";
2920   } else {
2921     $tech = strtolower( strtok($results[0][0],'/') ); // the technology.  ie: ZAP/g0 is ZAP
2922    
2923     if ($tech == "iax2") $tech = "iax"; // same thing, here
2924   }
2925   return $tech;
2926 }
2927
2928 //add trunk info to sip or iax table
2929 function core_trunks_addSipOrIax($config,$table,$channelid,$trunknum,$disable_flag=0) {
2930   global $db;
2931  
2932   $confitem['account'] = $channelid;
2933   $gimmieabreak = nl2br($config);
2934   $lines = split('<br />',$gimmieabreak);
2935   foreach ($lines as $line) {
2936     $line = trim($line);
2937     if (count(split('=',$line)) > 1) {
2938       $tmp = split('=',$line,2);
2939       $key=trim($tmp[0]);
2940       $value=trim($tmp[1]);
2941       if (isset($confitem[$key]) && !empty($confitem[$key]))
2942         $confitem[$key].="&".$value;
2943       else
2944         $confitem[$key]=$value;
2945     }
2946   }
2947   // rember 1=disabled so we start at 2 (1 + the first 1)
2948   $seq = 1;
2949   foreach($confitem as $k=>$v) {
2950     $seq = ($disable_flag == 1) ? 1 : $seq+1;
2951     $dbconfitem[]=array($k,$v,$seq);
2952   }
2953   $compiled = $db->prepare("INSERT INTO $table (id, keyword, data, flags) values ('9999$trunknum',?,?,?)");
2954   $result = $db->executeMultiple($compiled,$dbconfitem);
2955   if(DB::IsError($result)) {
2956     die_freepbx($result->getMessage()."<br><br>INSERT INTO $table (id, keyword, data, flags) values ('9999$trunknum',?,?,'$disable_flag')"); 
2957   }
2958 }
2959
2960 //get unique trunks
2961 function core_trunks_list($assoc = false) {
2962   // TODO: $assoc default to true, eventually..
2963
2964   global $db;
2965   global $amp_conf;
2966  
2967   if ($amp_conf["AMPDBENGINE"] == "sqlite3")
2968   {
2969     // TODO: sqlite work arround - diego
2970     // TODO: WILL NOT WORK, need to remove the usage of SUBSTRING
2971     // need to reorder the trunks in PHP code
2972     $sqlstr  = "SELECT t.variable, t.value, d.value state FROM `globals` t ";
2973     $sqlstr .= "JOIN (SELECT x.variable, x.value FROM globals x WHERE x.variable LIKE 'OUTDISABLE\_%') d ";
2974     $sqlstr .= "ON substring(t.variable,5) = substring(d.variable,12) WHERE t.variable LIKE 'OUT\_%' ";
2975     $sqlstr .= "UNION ALL ";
2976     $sqlstr .= "SELECT v.variable, v.value, concat(substring(v.value,1,0),'off') state  FROM `globals` v ";
2977     $sqlstr .= "WHERE v.variable LIKE 'OUT\_%' AND concat('OUTDISABLE_',substring(v.variable,5)) NOT IN ";
2978     $sqlstr .= " ( SELECT variable from globals WHERE variable LIKE 'OUTDISABLE\_%' ) ";
2979     $sqlstr .= "ORDER BY variable";
2980
2981     //$unique_trunks = sql("SELECT * FROM globals WHERE variable LIKE 'OUT_%' ORDER BY variable","getAll");
2982     $unique_trunks = sql($sqlstr,"getAll");
2983   }
2984   else
2985   {
2986     // we have to escape _ for mysql: normally a wildcard
2987     $sqlstr  = "SELECT t.variable, t.value, d.value state FROM `globals` t ";
2988     $sqlstr .= "JOIN (SELECT x.variable, x.value FROM globals x WHERE x.variable LIKE 'OUTDISABLE\\\_%') d ";
2989     $sqlstr .= "ON substring(t.variable,5) = substring(d.variable,12) WHERE t.variable LIKE 'OUT\\\_%' ";
2990     $sqlstr .= "UNION ALL ";
2991     $sqlstr .= "SELECT v.variable, v.value, concat(substring(v.value,1,0),'off') state  FROM `globals` v ";
2992     $sqlstr .= "WHERE v.variable LIKE 'OUT\\\_%' AND concat('OUTDISABLE_',substring(v.variable,5)) NOT IN ";
2993     $sqlstr .= " ( SELECT variable from globals WHERE variable LIKE 'OUTDISABLE\\\_%' ) ";
2994     $sqlstr .= "ORDER BY RIGHT( variable, LENGTH( variable ) - 4 )+0";
2995
2996     //$unique_trunks = sql("SELECT * FROM globals WHERE variable LIKE 'OUT\\\_%' ORDER BY RIGHT( variable, LENGTH( variable ) - 4 )+0","getAll");
2997     $unique_trunks = sql($sqlstr,"getAll");
2998   }
2999
3000   //if no trunks have ever been defined, then create the proper variables with the default zap trunk
3001   if (count($unique_trunks) == 0)
3002   {
3003     //If all trunks have been deleted from admin, dialoutids might still exist
3004     sql("DELETE FROM globals WHERE variable = 'DIALOUTIDS'");
3005  
3006     $glofields = array(array('OUT_1','ZAP/g0'),
3007               array('DIAL_OUT_1','9'),
3008               array('DIALOUTIDS','1'));
3009     $compiled = $db->prepare('INSERT INTO globals (variable, value) values (?,?)');
3010     $result = $db->executeMultiple($compiled,$glofields);
3011     if(DB::IsError($result))
3012     {
3013       die_freepbx($result->getMessage()."<br><br>".$sql);
3014     }
3015     $unique_trunks[] = array('OUT_1','ZAP/g0');
3016   }
3017   // asort($unique_trunks);
3018
3019   if ($assoc) {
3020     $trunkinfo = array();
3021
3022     foreach ($unique_trunks as $trunk) {
3023       list($tech,$name) = explode('/',$trunk[1]);
3024       $trunkinfo[$trunk[1]] = array(
3025         'name' => $name,
3026         'tech' => $tech,
3027         'globalvar' => $trunk[0], // ick
3028         'value' => $trunk[2], // ??  no idea what this is.
3029       ); 
3030     }
3031     return $trunkinfo;
3032   } else {
3033     return $unique_trunks;
3034   }
3035 }
3036
3037 //write the OUTIDS global variable (used in dialparties.agi)
3038 function core_trunks_writeoutids() {
3039   // we have to escape _ for mysql: normally a wildcard
3040   $unique_trunks = sql("SELECT variable FROM globals WHERE variable LIKE 'OUT\\\_%'","getAll");
3041
3042   $outids = null; // Start off with nothing
3043   foreach ($unique_trunks as $unique_trunk) {
3044     $outid = strtok($unique_trunk[0],"_");
3045     $outid = strtok("_");
3046     $outids .= $outid ."/";
3047   }
3048  
3049   sql("UPDATE globals SET value = '$outids' WHERE variable = 'DIALOUTIDS'");
3050 }
3051
3052 function core_trunks_addRegister($trunknum,$tech,$reg,$disable_flag=0) {
3053   sql("INSERT INTO $tech (id, keyword, data, flags) values ('9999999$trunknum','register','$reg','$disable_flag')");
3054 }
3055
3056
3057 function core_trunks_addDialRules($trunknum, $dialrules) {
3058   $values = array();
3059   $i = 1;
3060   foreach ($dialrules as $rule) {
3061     $values["rule".$i++] = $rule;
3062   }
3063  
3064   $conf = core_trunks_readDialRulesFile();
3065  
3066   // rewrite for this trunk
3067   $conf["trunk-".$trunknum] = $values;
3068  
3069   core_trunks_writeDialRulesFile($conf);
3070 }
3071
3072 function core_trunks_readDialRulesFile() {
3073   global $amp_conf;
3074   $localPrefixFile = $amp_conf['ASTETCDIR']."/localprefixes.conf";
3075  
3076   core_trunks_parse_conf($localPrefixFile, $conf, $section);
3077  
3078   return $conf;
3079 }
3080
3081 function core_trunks_writeDialRulesFile($conf) {
3082   global $amp_conf;
3083   $localPrefixFile = $amp_conf['ASTETCDIR']."/localprefixes.conf";
3084  
3085   $fd = fopen($localPrefixFile,"w");
3086   foreach ($conf as $section=>$values) {
3087     fwrite($fd, "[".$section."]\n");
3088     foreach ($values as $key=>$value) {
3089       fwrite($fd, $key."=".$value."\n");
3090     }
3091     fwrite($fd, "\n");
3092   }
3093   fclose($fd);
3094 }
3095
3096 function core_trunks_parse_conf($filename, &$conf, &$section) {
3097   if (is_null($conf)) {
3098     $conf = array();
3099   }
3100   if (is_null($section)) {
3101     $section = "general";
3102   }
3103  
3104   if (file_exists($filename)) {
3105     $fd = fopen($filename, "r");
3106     while ($line = fgets($fd, 1024)) {
3107       if (preg_match("/^\s*([a-zA-Z0-9-_]+)\s*=\s*(.*?)\s*([;#].*)?$/",$line,$matches)) {
3108         // name = value
3109         // option line
3110         $conf[$section][ $matches[1] ] = $matches[2];
3111       } else if (preg_match("/^\s*\[(.+)\]/",$line,$matches)) {
3112         // section name
3113         $section = strtolower($matches[1]);
3114       } else if (preg_match("/^\s*#include\s+(.*)\s*([;#].*)?/",$line,$matches)) {
3115         // include another file
3116        
3117         if ($matches[1][0] == "/") {
3118           // absolute path
3119           $filename = $matches[1];
3120         } else {
3121           // relative path
3122           $filename =  dirname($filename)."/".$matches[1];
3123         }
3124        
3125         core_trunks_parse_conf($filename, $conf, $section);
3126       }
3127     }
3128   }
3129 }
3130
3131 function core_trunks_getTrunkTrunkName($trunknum) {
3132   $results = sql("SELECT value FROM globals WHERE variable = 'OUT_".$trunknum."'","getAll");
3133   if (!$results) {
3134     return false;
3135   }
3136  
3137   if(strpos($results[0][0],"AMP:") === 0) {  //custom trunks begin with AMP:
3138     $tname = substr($results[0][0],4);
3139   } else {
3140   strtok($results[0][0],'/');
3141     $tname = strtok('/'); // the text _after_ technology.  ie: ZAP/g0 is g0
3142   }
3143   return $tname;
3144 }
3145
3146 //get and print peer details (prefixed with 4 9's)
3147 function core_trunks_getTrunkPeerDetails($trunknum) {
3148   global $db;
3149  
3150   $tech = core_trunks_getTrunkTech($trunknum);
3151  
3152   if ($tech == "zap") return ""; // zap has no details
3153  
3154   $results = sql("SELECT keyword,data FROM $tech WHERE id = '9999$trunknum' ORDER BY flags, keyword DESC","getAll");
3155  
3156   foreach ($results as $result) {
3157     if ($result[0] != 'account') {
3158       if (isset($confdetail))
3159         $confdetail .= $result[0] .'='. $result[1] . "\n";
3160       else
3161         $confdetail = $result[0] .'='. $result[1] . "\n";
3162     }
3163   }
3164   return $confdetail;
3165 }
3166
3167 //get trunk user context (prefixed with 5 9's)
3168 function core_trunks_getTrunkUserContext($trunknum) {
3169   $tech = core_trunks_getTrunkTech($trunknum);
3170   if ($tech == "zap") return ""; // zap has no account
3171  
3172   $results = sql("SELECT keyword,data FROM $tech WHERE id = '99999$trunknum'","getAll");
3173
3174   foreach ($results as $result) {
3175     if ($result[0] == 'account') {
3176       $account = $result[1];
3177     }
3178   }
3179   return isset($account)?$account:null;
3180 }
3181
3182 //get and print user config (prefixed with 5 9's)
3183 function core_trunks_getTrunkUserConfig($trunknum) {
3184   global $db;
3185  
3186   $tech = core_trunks_getTrunkTech($trunknum);
3187  
3188   if ($tech == "zap") return ""; // zap has no details
3189  
3190   $results = sql("SELECT keyword,data FROM $tech WHERE id = '99999$trunknum' ORDER BY flags, keyword DESC","getAll");
3191
3192   foreach ($results as $result) {
3193     if ($result[0] != 'account') {
3194       if (isset($confdetail))
3195         $confdetail .= $result[0] .'='. $result[1] . "\n";
3196       else
3197         $confdetail = $result[0] .'='. $result[1] . "\n";
3198     }
3199   }
3200   return isset($confdetail)?$confdetail:null;
3201 }
3202
3203 //get trunk account register string
3204 function core_trunks_getTrunkRegister($trunknum) {
3205   $tech = core_trunks_getTrunkTech($trunknum);
3206  
3207   if ($tech == "zap") return ""; // zap has no register
3208  
3209   $results = sql("SELECT keyword,data FROM $tech WHERE id = '9999999$trunknum'","getAll");
3210
3211   foreach ($results as $result) {
3212       $register = $result[1];
3213   }
3214   return isset($register)?$register:null;
3215 }
3216
3217 function core_trunks_getDialRules($trunknum) {
3218   $conf = core_trunks_readDialRulesFile();
3219   if (isset($conf["trunk-".$trunknum])) {
3220     return $conf["trunk-".$trunknum];
3221   }
3222   return false;
3223 }
3224
3225 //get outbound routes for a given trunk
3226 function core_trunks_gettrunkroutes($trunknum) {
3227   $sql_code = "SELECT DISTINCT SUBSTRING(context,7) route ,args trunk FROM extensions WHERE context LIKE 'outrt-%' AND
3228     (args LIKE 'dialout-trunk,%' OR args LIKE 'dialout-enum,%' OR args LIKE 'dialout-dundi,%') ORDER BY context,priority";
3229   $results = sql( $sql_code, "getAll" ,DB_FETCHMODE_ASSOC);
3230   $routeseq = array();
3231   foreach ($results as $entry) {
3232     $pos1 = strpos($entry['trunk'],',')+1;
3233     $routeseq[$entry['route']][] = substr($entry['trunk'],$pos1,strpos($entry['trunk'],',',$pos1)-$pos1);
3234   }
3235   $routes = array();
3236   foreach ($routeseq as $key => $value) {
3237     $pos = array_search($trunknum, array_values(array_unique($value)));
3238     if ($pos !== false) {
3239       $routes[$key] = $pos+1; // start at 1, not 0
3240     }
3241   }
3242   return $routes;
3243 }
3244
3245 function core_trunks_deleteDialRules($trunknum) {
3246   $conf = core_trunks_readDialRulesFile();
3247  
3248   // remove rules for this trunk
3249   unset($conf["trunk-".$trunknum]);
3250  
3251   core_trunks_writeDialRulesFile($conf);
3252 }
3253
3254 /* end page.trunks.php functions */
3255
3256
3257 /* begin page.routing.php functions */
3258
3259 //get unique outbound route names
3260 function core_routing_getroutenames()
3261 {
3262   global $amp_conf;
3263  
3264   if ($amp_conf["AMPDBENGINE"] == "sqlite3")
3265   {
3266     // SUBSTRING is not supported under sqlite3, we need to filter
3267     // this in php. I am not sure why "6" and not "7"
3268     // but I don't really care -> it works :)
3269     $results = sql("SELECT DISTINCT context FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context ","getAll");
3270     foreach( array_keys($results) as $idx )
3271     {
3272        $results[$idx][0] = substr( $results[$idx][0], 6);
3273     }
3274   }
3275   else
3276   {
3277     // we SUBSTRING() to remove "outrt-"
3278     $results = sql("SELECT DISTINCT SUBSTRING(context,7) FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context ","getAll");
3279   }
3280
3281
3282   //TODO: This needs to be yanked, should be in the upgrade script somewhere not here
3283   //
3284   if (count($results) == 0) {
3285     // see if they're still using the old dialprefix method
3286     $results = sql("SELECT variable,value FROM globals WHERE variable LIKE 'DIAL\\\_OUT\\\_%'","getAll");
3287     // we SUBSTRING() to remove "outrt-"
3288    
3289     if (count($results) > 0) {
3290       // yes, they are using old method, let's update
3291      
3292       // get the default trunk
3293       $results_def = sql("SELECT value FROM globals WHERE variable = 'OUT'","getAll");
3294      
3295       if (preg_match("/{OUT_(\d+)}/", $results_def[0][0], $matches)) {
3296         $def_trunk = $matches[1];
3297       } else {
3298         $def_trunk = "";
3299       }
3300      
3301       $default_patterns = array(  // default patterns that used to be in extensions.conf
3302             "NXXXXXX",
3303             "NXXNXXXXXX",
3304             "1800NXXXXXX",
3305             "1888NXXXXXX",
3306             "1877NXXXXXX",
3307             "1866NXXXXXX",
3308             "1NXXNXXXXXX",
3309             "011.",
3310             "911",
3311             "411",
3312             "311",
3313             );
3314      
3315       foreach ($results as $temp) {
3316         // temp[0] is "DIAL_OUT_1"
3317         // temp[1] is the dial prefix
3318        
3319         $trunknum = substr($temp[0],9);
3320        
3321         $name = "route".$trunknum;
3322        
3323         $trunks = array(1=>"OUT_".$trunknum); // only one trunk to use
3324        
3325         $patterns = array();
3326         foreach ($default_patterns as $pattern) {
3327           $patterns[] = $temp[1]."|".$pattern;
3328         }
3329        
3330         if ($trunknum == $def_trunk) {
3331           // this is the default trunk, add the patterns with no prefix
3332           $patterns = array_merge($patterns, $default_patterns);
3333         }
3334        
3335         // add this as a new route
3336         core_routing_add($name, $patterns, $trunks,"new");
3337       }
3338      
3339      
3340       // delete old values
3341       sql("DELETE FROM globals WHERE (variable LIKE 'DIAL\\\_OUT\\\_%') OR (variable = 'OUT') ");
3342
3343       // we need to re-generate extensions_additional.conf
3344       // i'm not sure how to do this from here
3345      
3346       // re-run our query
3347       $results = sql("SELECT DISTINCT SUBSTRING(context,7) FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context ","getAll");
3348       // we SUBSTRING() to remove "outrt-"
3349     }
3350    
3351   } // else, it just means they have no routes.
3352  
3353   return $results;
3354 }
3355
3356 function core_routing_setroutepriority($routepriority, $reporoutedirection, $reporoutekey)
3357 {
3358   global $db, $amp_conf;
3359   $counter=-1;
3360   foreach ($routepriority as $tresult)
3361   {
3362     $counter++;
3363     if (($counter==($reporoutekey-1)) && ($reporoutedirection=="up")) {
3364       // swap this one with the one before (move up)
3365       $temproute = $routepriority[$counter];
3366       $routepriority[ $counter ] = $routepriority[ $counter+1 ];
3367       $routepriority[ $counter+1 ] = $temproute;
3368      
3369     } else if (($counter==($reporoutekey)) && ($reporoutedirection=="down")) {
3370       // swap this one with the one after (move down)
3371       $temproute = $routepriority[ $counter+1 ];
3372       $routepriority[ $counter+1 ] = $routepriority[ $counter ];
3373       $routepriority[ $counter ] = $temproute;
3374     }
3375   }
3376   unset($temptrunk);
3377   $routepriority = array_values($routepriority); // resequence our numbers
3378   $counter=0;
3379   foreach ($routepriority as $tresult) {
3380     $order=core_routing_setroutepriorityvalue($counter++);
3381     $sql = sprintf("Update extensions set context='outrt-%s-%s' WHERE context='outrt-%s'",$order,substr($tresult[0],4), $tresult[0]);
3382     $result = $db->query($sql);
3383     if(DB::IsError($result)) {     
3384       die_freepbx($result->getMessage());
3385     }
3386   }
3387  
3388   // Delete and readd the outbound-allroutes entries
3389   $sql = "delete from  extensions WHERE context='outbound-allroutes'";
3390   $result = $db->query($sql);
3391   if(DB::IsError($result)) {
3392           die_freepbx($result->getMessage().$sql);
3393   }
3394  
3395   $sql = "SELECT DISTINCT context FROM extensions WHERE context like 'outrt-%' ORDER BY context";
3396   $results = $db->getAll($sql);
3397   if(DB::IsError($results)) {
3398     die_freepbx($results->getMessage());
3399   }
3400
3401   $priority_loops=1; 
3402   foreach ($results as $row) {
3403     $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr, flags) VALUES ";
3404     $sql .= "('outbound-allroutes', ";
3405     $sql .= "'include', ";
3406     $sql .= "'".$priority_loops++."', ";
3407     $sql .= "'".$row[0]."', ";
3408     $sql .= "'', ";
3409     $sql .= "'', ";
3410     $sql .= "'2')";
3411  
3412     //$sql = sprintf("Update extensions set application='outrt-%s-%s' WHERE context='outbound-allroutes' and  application='outrt-%s'",$order,substr($tresult[0],4), $tresult[0]);
3413     $result = $db->query($sql);
3414     if(DB::IsError($result)) {     
3415       die_freepbx($result->getMessage(). $sql);
3416     }
3417   }
3418  
3419   if ( $amp_conf["AMPDBENGINE"] == "sqlite3")
3420     $sql = "SELECT DISTINCT context FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context ";
3421   else
3422     $sql = "SELECT DISTINCT SUBSTRING(context,7) FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context ";
3423
3424         // we SUBSTRING() to remove "outrt-"
3425         $routepriority = $db->getAll($sql);
3426         if(DB::IsError($routepriority)) {
3427                 die_freepbx($routepriority->getMessage());
3428         }
3429
3430   // TODO: strip the context on the sqlite3 backend
3431   // not sure where does it effects, since this is working on my setup...
3432   // welcome to funky town
3433         return ($routepriority);
3434 }
3435
3436 function core_routing_setroutepriorityvalue($key)
3437 {
3438   $key=$key+1;
3439   if ($key<10)
3440     $prefix = sprintf("00%d",$key);
3441   else if ((9<$key)&&($key<100))
3442     $prefix = sprintf("0%d",$key);
3443   else if ($key>100)
3444     $prefix = sprintf("%d",$key);
3445   return ($prefix);
3446 }
3447
3448
3449 function core_routing_add($name, $patterns, $trunks, $method, $pass, $emergency = "", $intracompany = "", $mohsilence = "") {
3450
3451   global $db;
3452
3453   $trunktech=array();
3454
3455   //Retrieve each trunk tech for later lookup
3456   $sql="select * from globals WHERE variable LIKE 'OUT\\_%'";
3457         $result = $db->getAll($sql);
3458         if(DB::IsError($result)) {
3459     die_freepbx($result->getMessage());
3460   }
3461   foreach($result as $tr) {
3462     $tech = strtok($tr[1], "/");
3463     $trunktech[$tr[0]]=$tech;
3464   }
3465  
3466   if ($method=="new") {
3467     $sql="select DISTINCT context FROM extensions WHERE context LIKE 'outrt-%' ORDER BY context";
3468     $routepriority = $db->getAll($sql);
3469     if(DB::IsError($result)) {
3470       die_freepbx($result->getMessage());
3471     }
3472     $order=core_routing_setroutepriorityvalue(count($routepriority));
3473     $name = sprintf ("%s-%s",$order,$name);
3474   }
3475   $trunks = array_values($trunks); // probably already done, but it's important for our dialplan
3476
3477  
3478   foreach ($patterns as $pattern) {
3479     if (false !== ($pos = strpos($pattern,"|"))) {
3480       // we have a | meaning to not pass the digits on
3481       // (ie, 9|NXXXXXX should use the pattern _9NXXXXXX but only pass NXXXXXX, not the leading 9)
3482      
3483       $pattern = str_replace("|","",$pattern); // remove all |'s
3484       $exten = "EXTEN:".$pos; // chop off leading digit
3485     } else {
3486       // we pass the full dialed number as-is
3487       $exten = "EXTEN";
3488     }
3489    
3490     if (!preg_match("/^[0-9*]+$/",$pattern)) {
3491       // note # is not here, as asterisk doesn't recoginize it as a normal digit, thus it requires _ pattern matching
3492      
3493       // it's not strictly digits, so it must have patterns, so prepend a _
3494       $pattern = "_".$pattern;
3495     }
3496    
3497     // 1st priority is emergency dialing variable (if set)
3498     if(!empty($emergency)) {
3499       $startpriority = 1;
3500       $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr) VALUES ";
3501       $sql .= "('outrt-".$name."', ";
3502       $sql .= "'".$pattern."', ";
3503       $sql .= "'".$startpriority."', ";
3504       $sql .= "'SetVar', ";
3505       $sql .= "'EMERGENCYROUTE=YES', ";
3506       $sql .= "'Use Emergency CID for device')";
3507       $result = $db->query($sql);
3508       if(DB::IsError($result)) {
3509         die_freepbx($result->getMessage());
3510       }
3511     } else {
3512       $startpriority = 0;
3513     }
3514
3515     // Next Priority (either first or second depending on above)
3516     if(!empty($intracompany)) {
3517          $startpriority += 1;
3518          $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr) VALUES ";
3519          $sql .= "('outrt-".$name."', ";
3520          $sql .= "'".$pattern."', ";
3521          $sql .= "'".$startpriority."', ";
3522          $sql .= "'SetVar', ";
3523          $sql .= "'INTRACOMPANYROUTE=YES', ";
3524          $sql .= "'Preserve Intenal CID Info')";
3525          $result = $db->query($sql);
3526         if(DB::IsError($result)) {
3527              die_freepbx($result->getMessage());
3528         }
3529     }
3530
3531     // Next Priority (either first, second or third depending on above)
3532     if(!empty($mohsilence) && trim($mohsilence) != 'default') {
3533          $startpriority += 1;
3534          $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr) VALUES ";
3535          $sql .= "('outrt-".$name."', ";
3536          $sql .= "'".$pattern."', ";
3537          $sql .= "'".$startpriority."', ";
3538          $sql .= "'SetVar', ";
3539          $sql .= "'MOHCLASS=".$mohsilence."', ";
3540          $sql .= "'Do not play moh on this route')";
3541          $result = $db->query($sql);
3542         if(DB::IsError($result)) {
3543              die_freepbx($result->getMessage());
3544         }
3545     }
3546
3547     $first_trunk = 1;
3548     foreach ($trunks as $priority => $trunk) {
3549       $priority += $startpriority;
3550       $priority += 1; // since arrays are 0-based, but we want priorities to start at 1
3551      
3552       $sql = "INSERT INTO extensions (context, extension, priority, application, args) VALUES ";
3553       $sql .= "('outrt-".$name."', ";
3554       $sql .= "'".$pattern."', ";
3555       $sql .= "'".$priority."', ";
3556       $sql .= "'Macro', ";
3557       if ($first_trunk)
3558         $pass_str = $pass;
3559       else
3560         $pass_str = "";
3561
3562       if ($trunktech[$trunk] == "ENUM") {
3563         $sql .= "'dialout-enum,".substr($trunk,4).",\${".$exten."},".$pass_str."'"; // cut off OUT_ from $trunk
3564       } else if ($trunktech[$trunk] == "DUNDI") {
3565         $sql .= "'dialout-dundi,".substr($trunk,4).",\${".$exten."},".$pass_str."'"; // cut off OUT_ from $trunk
3566       } else {
3567         $sql .= "'dialout-trunk,".substr($trunk,4).",\${".$exten."},".$pass_str."'"; // cut off OUT_ from $trunk
3568       }
3569       $sql .= ")";
3570      
3571       $result = $db->query($sql);
3572       if(DB::IsError($result)) {
3573         die_freepbx($result->getMessage());
3574       }
3575       //To identify the first trunk in a pattern
3576       //so that passwords are in the first trunk in
3577       //each pattern
3578       $first_trunk = 0;
3579     }
3580    
3581     $priority += 1;
3582     $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr) VALUES ";
3583     $sql .= "('outrt-".$name."', ";
3584     $sql .= "'".$pattern."', ";
3585     $sql .= "'".$priority."', ";
3586     $sql .= "'Macro', ";
3587     $sql .= "'outisbusy', ";
3588     $sql .= "'No available circuits')";
3589    
3590     $result = $db->query($sql);
3591     if(DB::IsError($result)) {
3592       die_freepbx($result->getMessage());
3593     }
3594   }
3595
3596  
3597   // add an include=>outrt-$name  to [outbound-allroutes]:
3598  
3599   // we have to find the first available priority.. priority doesn't really matter for the include, but
3600   // there is a unique index on (context,extension,priority) so if we don't do this we can't put more than
3601   // one route in the outbound-allroutes context.
3602   $sql = "SELECT priority FROM extensions WHERE context = 'outbound-allroutes' AND extension = 'include'";
3603   $results = $db->getAll($sql);
3604   if(DB::IsError($results)) {
3605     die_freepbx($results->getMessage());
3606   }
3607   $priorities = array();
3608   foreach ($results as $row) {
3609     $priorities[] = $row[0];
3610   }
3611   for ($priority = 1; in_array($priority, $priorities); $priority++);
3612  
3613   // $priority should now be the lowest available number
3614  
3615   $sql = "INSERT INTO extensions (context, extension, priority, application, args, descr, flags) VALUES ";
3616   $sql .= "('outbound-allroutes', ";
3617   $sql .= "'include', ";
3618   $sql .= "'".$priority."', ";
3619   $sql .= "'outrt-".$name."', ";
3620   $sql .= "'', ";
3621   $sql .= "'', ";
3622   $sql .= "'2')";
3623  
3624   $result = $db->query($sql);
3625   if(DB::IsError($result)) {
3626     die_freepbx($priority.$result->getMessage());
3627   }
3628  
3629 }
3630
3631 function core_routing_edit($name, $patterns, $trunks, $pass, $emergency="", $intracompany = "", $mohsilence="") {
3632   core_routing_del($name);
3633   core_routing_add($name, $patterns, $trunks,"edit", $pass, $emergency, $intracompany, $mohsilence);
3634 }
3635
3636 function core_routing_del($name) {
3637   global $db;
3638   $sql = "DELETE FROM extensions WHERE context = 'outrt-".$name."'";
3639   $result = $db->query($sql);
3640   if(DB::IsError($result)) {
3641     die_freepbx($result->getMessage());
3642   }
3643  
3644   $sql = "DELETE FROM extensions WHERE context = 'outbound-allroutes' AND application = 'outrt-".$name."' ";
3645   $result = $db->query($sql);
3646   if(DB::IsError($result)) {
3647     die_freepbx($result->getMessage());
3648   }
3649  
3650   return $result;
3651 }
3652
3653 /* Delete all occurences of the specified trunk from all routes that may use it
3654  */
3655 function core_routing_trunk_del($trunknum) {
3656   global $db;
3657
3658   $sql = "DELETE FROM `extensions` WHERE `application` = 'Macro' AND `context` LIKE 'outrt-%' AND `args` LIKE 'dialout-%,$trunknum,%'";
3659   $result = $db->query($sql);
3660 }
3661
3662 function core_routing_rename($oldname, $newname) {
3663   global $db;
3664
3665   $route_prefix=substr($oldname,0,4);
3666   $newname=$route_prefix.$newname;
3667   $sql = "SELECT context FROM extensions WHERE context = 'outrt-".$newname."'";
3668   $results = $db->getAll($sql);
3669   if (count($results) > 0) {
3670     // there's already a route with this name
3671     return false;
3672   }
3673  
3674   $sql = "UPDATE extensions SET context = 'outrt-".$newname."' WHERE context = 'outrt-".$oldname."'";
3675   $result = $db->query($sql);
3676   if(DB::IsError($result)) {
3677     die_freepbx($result->getMessage());
3678   }
3679         $mypriority=sprintf("%d",$route_prefix); 
3680   $sql = "UPDATE extensions SET application = 'outrt-".$newname."', priority = '$mypriority' WHERE context = 'outbound-allroutes' AND application = 'outrt-".$oldname."' ";
3681   $result = $db->query($sql);
3682   if(DB::IsError($result)) {
3683     die_freepbx($result->getMessage());
3684   }
3685  
3686   return true;
3687 }
3688
3689 //get unique outbound route patterns for a given context
3690 function core_routing_getroutepatterns($route) {
3691   global $db;
3692   $sql = "SELECT extension, args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'dialout-trunk%' OR args LIKE 'dialout-enum%' OR args LIKE 'dialout-dundi%') ORDER BY extension ";
3693   $results = $db->getAll($sql);
3694   if(DB::IsError($results)) {
3695     die_freepbx($results->getMessage());
3696   }
3697  
3698   $patterns = array();
3699   foreach ($results as $row) {
3700     if ($row[0][0] == "_") {
3701       // remove leading _
3702       $pattern = substr($row[0],1);
3703     } else {
3704       $pattern = $row[0];
3705     }
3706    
3707     if (preg_match("/{EXTEN:(\d+)}/", $row[1], $matches)) {
3708       // this has a digit offset, we need to insert a |
3709       $pattern = substr($pattern,0,$matches[1])."|".substr($pattern,$matches[1]);
3710     }
3711    
3712     $patterns[] = $pattern;
3713   }
3714   return array_unique($patterns);
3715 }
3716
3717 //get unique outbound route trunks for a given context
3718 function core_routing_getroutetrunks($route) {
3719   global $db;
3720   $sql = "SELECT DISTINCT args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'dialout-trunk,%' OR args LIKE 'dialout-enum,%' OR args LIKE 'dialout-dundi,%') ORDER BY CAST(priority as UNSIGNED) ";
3721   $results = $db->getAll($sql);
3722   if(DB::IsError($results)) {
3723     die_freepbx($results->getMessage());
3724   }
3725  
3726   $trunks = array();
3727   foreach ($results as $row) {
3728     if (preg_match('/^dialout-trunk,(\d+)/', $row[0], $matches)) {
3729       // check in_array -- even though we did distinct
3730       // we still might get ${EXTEN} and ${EXTEN:1} if they used | to split a pattern
3731       if (!in_array("OUT_".$matches[1], $trunks)) {
3732         $trunks[] = "OUT_".$matches[1];
3733       }
3734     } else if (preg_match('/^dialout-enum,(\d+)/', $row[0], $matches)) {
3735       if (!in_array("OUT_".$matches[1], $trunks)) {
3736         $trunks[] = "OUT_".$matches[1];
3737       }
3738     } else if (preg_match('/^dialout-dundi,(\d+)/', $row[0], $matches)) {
3739       if (!in_array("OUT_".$matches[1], $trunks)) {
3740         $trunks[] = "OUT_".$matches[1];
3741       }
3742     }
3743   }
3744   return $trunks;
3745 }
3746
3747
3748 //get password for this route
3749 function core_routing_getroutepassword($route) {
3750   global $db;
3751   $sql = "SELECT DISTINCT args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'dialout-trunk,%' OR args LIKE 'dialout-enum,%' OR args LIKE 'dialout-dundi,%') ORDER BY CAST(priority as UNSIGNED) ";
3752   $results = $db->getOne($sql);
3753   if(DB::IsError($results)) {
3754     die_freepbx($results->getMessage());
3755   }
3756   if (preg_match('/^.*,.*,.*,(\d+|\/\S+)/', $results, $matches)) {
3757     $password = $matches[1];
3758   } else {
3759     $password = "";
3760   }
3761  
3762   return $password;
3763 }
3764
3765 //get emergency state for this route
3766 function core_routing_getrouteemergency($route) {
3767   global $db;
3768   $sql = "SELECT DISTINCT args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'EMERGENCYROUTE%') ";
3769   $results = $db->getOne($sql);
3770   if(DB::IsError($results)) {
3771     die_freepbx($results->getMessage());
3772   }
3773   if (preg_match('/^.*=(.*)/', $results, $matches)) {
3774     $emergency = $matches[1];
3775   } else {
3776     $emergency = "";
3777   }
3778  
3779   return $emergency;
3780 }
3781
3782 //get intracompany routing status for this route
3783 function core_routing_getrouteintracompany($route) {
3784
3785        global $db;
3786        $sql = "SELECT DISTINCT args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'INTRACOMPANYROUTE%') ";
3787        $results = $db->getOne($sql);
3788        if(DB::IsError($results)) {
3789                die_freepbx($results->getMessage());
3790        }
3791        if (preg_match('/^.*=(.*)/', $results, $matches)) {
3792                $intracompany = $matches[1];
3793        } else {
3794                $intracompany = "";
3795        }
3796        return $intracompany;
3797 }
3798
3799 //get mohsilence routing status for this route
3800 function core_routing_getroutemohsilence($route) {
3801
3802        global $db;
3803        $sql = "SELECT DISTINCT args FROM extensions WHERE context = 'outrt-".$route."' AND (args LIKE 'MOHCLASS%') ";
3804        $results = $db->getOne($sql);
3805        if(DB::IsError($results)) {
3806                die_freepbx($results->getMessage());
3807        }
3808        if (preg_match('/^.*=(.*)/', $results, $matches)) {
3809                $mohsilence = $matches[1];
3810        } else {
3811                $mohsilence = "";
3812        }
3813        return $mohsilence;
3814 }
3815
3816 function general_get_zonelist() {
3817   return array(
3818  array ( "name" => "Austria",  "iso" => "at", "conf" => "ringcadence = 1000,5000\ndial = 420\nbusy = 420/400,0/400\nring = 420/1000,0/5000\ncongestion = 420/200,0/200\ncallwaiting = 420/40,0/1960\ndialrecall = 420\nrecord = 1400/80,0/14920\ninfo = 950/330,1450/330,1850/330,0/1000\nstutter = 380+420\n"),
3819  array ( "name" => "Australia",  "iso" => "au", "conf" => "ringcadence = 400,200,400,2000\ndial = 413+438\nbusy = 425/375,0/375\nring = 413+438/400,0/200,413+438/400,0/2000\ncongestion = 425/375,0/375,420/375,0/375\ncallwaiting = 425/200,0/200,425/200,0/4400\ndialrecall = 413+438\nrecord = !425/1000,!0/15000,425/360,0/15000\ninfo = 425/2500,0/500\nstd = !525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100\nfacility = 425\nstutter = 413+438/100,0/40\nringmobile = 400+450/400,0/200,400+450/400,0/2000\n"),
3820  array ( "name" => "Brazil",  "iso" => "br", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/250,0/250\nring = 425/1000,0/4000\ncongestion = 425/250,0/250,425/750,0/250\ncallwaiting = 425/50,0/1000\ndialrecall = 350+440\nrecord = 425/250,0/250\ninfo = 950/330,1400/330,1800/330\nstutter = 350+440\n"),
3821  array ( "name" => "Belgium",  "iso" => "be", "conf" => "ringcadence = 1000,3000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/3000\ncongestion = 425/167,0/167\ncallwaiting = 1400/175,0/175,1400/175,0/3500\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = 900/330,1400/330,1800/330,0/1000\nstutter = 425/1000,0/250\n"),
3822  array ( "name" => "Switzerland",  "iso" => "ch", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/200,0/200,425/200,0/4000\ndialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\nrecord = 1400/80,0/15000\ninfo = 950/330,1400/330,1800/330,0/1000\nstutter = 425+340/1100,0/1100\n"),
3823  array ( "name" => "Chile",  "iso" => "cl", "conf" => "ringcadence = 1000,3000\ndial = 400\nbusy = 400/500,0/500\nring = 400/1000,0/3000\ncongestion = 400/200,0/200\ncallwaiting = 400/250,0/8750\ndialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400\nrecord = 1400/500,0/15000\ninfo = 950/333,1400/333,1800/333,0/1000\nstutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400\n"),
3824  array ( "name" => "China",  "iso" => "cn", "conf" => "ringcadence = 1000,4000\ndial = 450\nbusy = 450/350,0/350\nring = 450/1000,0/4000\ncongestion = 450/700,0/700\ncallwaiting = 450/400,0/4000\ndialrecall = 450\nrecord = 950/400,0/10000\ninfo = 450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400\nstutter = 450+425\n"),
3825  array ( "name" => "Czech Republic",  "iso" => "cz", "conf" => "ringcadence = 1000,4000\ndial = 425/330,0/330,425/660,0/660\nbusy = 425/330,0/330\nring = 425/1000,0/4000\ncongestion = 425/165,0/165\ncallwaiting = 425/330,0/9000\ndialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660\nrecord = 1400/500,0/14000\ninfo = 950/330,0/30,1400/330,0/30,1800/330,0/1000\nstutter = 425/450,0/50\n"),
3826  array ( "name" => "Germany",  "iso" => "de", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/480,0/480\nring = 425/1000,0/4000\ncongestion = 425/240,0/240\ncallwaiting = !425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0\ndialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\nrecord = 1400/80,0/15000\ninfo = 950/330,1400/330,1800/330,0/1000\nstutter = 425+400\n"),
3827  array ( "name" => "Denmark",  "iso" => "dk", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = !425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0\ndialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\nrecord = 1400/80,0/15000\ninfo = 950/330,1400/330,1800/330,0/1000\nstutter = 425/450,0/50\n"),
3828  array ( "name" => "Estonia",  "iso" => "ee", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/300,0/300\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 950/650,0/325,950/325,0/30,1400/1300,0/2600\ndialrecall = 425/650,0/25\nrecord = 1400/500,0/15000\ninfo = 950/650,0/325,950/325,0/30,1400/1300,0/2600\nstutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\n"),
3829  array ( "name" => "Spain",  "iso" => "es", "conf" => "ringcadence = 1500,3000\ndial = 425\nbusy = 425/200,0/200\nring = 425/1500,0/3000\ncongestion = 425/200,0/200,425/200,0/200,425/200,0/600\ncallwaiting = 425/175,0/175,425/175,0/3500\ndialrecall = !425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425\nrecord = 1400/500,0/15000\ninfo = 950/330,0/1000\ndialout = 500\n\n"),
3830  array ( "name" => "Finland",  "iso" => "fi", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/300,0/300\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/150,0/150,425/150,0/8000\ndialrecall = 425/650,0/25\nrecord = 1400/500,0/15000\ninfo = 950/650,0/325,950/325,0/30,1400/1300,0/2600\nstutter = 425/650,0/25\n"),
3831  array ( "name" => "France",  "iso" => "fr", "conf" => "ringcadence = 1500,3500\ndial = 440\nbusy = 440/500,0/500\nring = 440/1500,0/3500\ncongestion = 440/250,0/250\ncallwait = 440/300,0/10000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330\nstutter = !440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440\n"),
3832  array ( "name" => "Greece",  "iso" => "gr", "conf" => "ringcadence = 1000,4000\ndial = 425/200,0/300,425/700,0/800\nbusy = 425/300,0/300\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/150,0/150,425/150,0/8000\ndialrecall = 425/650,0/25\nrecord = 1400/400,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0\nstutter = 425/650,0/25\n"),
3833 array ( "name" => "Hong Kong", "iso" => "hk", "conf" => "ringcadence = 400,200,400,3000\ndial = 350+440\nbusy = 480+620/500,0/500\nring = 440+480/400,0/200,440+480/400,0/3000\ncongestion = 480+620/250,0/250\ncallwaiting = 440/300,0/10000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3834  array ( "name" => "Hungary",  "iso" => "hu", "conf" => "ringcadence = 1250,3750\ndial = 425\nbusy = 425/300,0/300\nring = 425/1250,0/3750\ncongestion = 425/300,0/300\ncallwaiting = 425/40,0/1960\ndialrecall = 425+450\nrecord = 1400/400,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0\nstutter = 350+375+400\n"),
3835  array ( "name" => "India",  "iso" => "in", "conf" => "ringcadence = 400,200,400,2000\ndial = 400*25\nbusy = 400/750,0/750\nring = 400*25/400,0/200,400*25/400,0/2000\ncongestion = 400/250,0/250\ncallwaiting = 400/200,0/100,400/200,0/7500\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0/1000\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3836  array ( "name" => "Israel",  "iso" => "il", "conf" => "ringcadence = 1000,3000\ndial = 414\nbusy = 414/500,0/500\nring = 414/1000,0/3000\ncongestion = 414/250,0/250\ncallwaiting = 414/100,0/100,414/100,0/100,414/600,0/3000 \ndialrecall = !414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414\nrecord = 1400/500,0/15000\ninfo = 1000/330,1400/330,1800/330,0/1000\nstutter = !414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414 \n"),
3837  array ( "name" => "Italy",  "iso" => "it", "conf" => "ringcadence = 1000,4000\ndial = 425/200,0/200,425/600,0/1000\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/400,0/100,425/250,0/100,425/150,0/14000\ndialrecall = 470/400,425/400\nrecord = 1400/400,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0\nstutter = 470/400,425/400\n"),
3838  array ( "name" => "Japan",  "iso" => "jp", "conf" => "ringcadence = 1000,2000\ndial = 400\nbusy = 400/500,0/500\nring = 400+15/1000,0/2000\ncongestion = 400/500,0/500\ncallwaiting = 400+16/500,0/8000\ndialrecall = !400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400\n"),
3839  array ( "name" => "Lithuania",  "iso" => "lt", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/350,0/350\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/150,0/150,425/150,0/4000\ndialrecall = 425/500,0/50\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0\nstutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\n"),
3840  array ( "name" => "Mexico",  "iso" => "mx", "conf" => "ringcadence = 2000,4000\ndial = 425\nbusy = 425/250,0/250\nring = 425/1000,0/4000\ncongestion = 425/250,0/250\ncallwaiting = 425/200,0/600,425/200,0/10000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = 950/330,0/30,1400/330,0/30,1800/330,0/1000\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3841  array ( "name" => "Netherlands",  "iso" => "nl", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/250,0/250\ncallwaiting = 425/500,0/9500\ndialrecall = 425/500,0/50\nrecord = 1400/500,0/15000\ninfo = 950/330,1400/330,1800/330,0/1000\nstutter = 425/500,0/50\n"),
3842  array ( "name" => "Norway",  "iso" => "no", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/200,0/200\ncallwaiting = 425/200,0/600,425/200,0/10000\ndialrecall = 470/400,425/400\nrecord = 1400/400,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0\nstutter = 470/400,425/400\n"),
3843  array ( "name" => "New Zealand",  "iso" => "nz", "conf" => "ringcadence = 400,200,400,2000\ndial = 400\nbusy = 400/250,0/250\nring = 400+450/400,0/200,400+450/400,0/2000\ncongestion = 400/375,0/375\ncallwaiting = !400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200\ndialrecall = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,400\nrecord = 1400/425,0/15000\ninfo = 400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400\nstutter = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400\n"),
3844  array ( "name" => "Philippines",  "iso" => "phl", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 480+620/500,0/500\nring = 425+480/1000,0/4000\ncongestion = 480+620/250,0/250\ncallwaiting = 440/300,0/10000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3845  array ( "name" => "Poland",  "iso" => "pl", "conf" => "ringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/500,0/500\ncallwaiting = 425/150,0/150,425/150,0/4000\ndialrecall = 425/500,0/50\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000\nstutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\n"),
3846  array ( "name" => "Portugal",  "iso" => "pt", "conf" => "ringcadence = 1000,5000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/5000\ncongestion = 425/200,0/200\ncallwaiting = 440/300,0/10000\ndialrecall = 425/1000,0/200\nrecord = 1400/500,0/15000\ninfo = 950/330,1400/330,1800/330,0/1000\nstutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\n"),
3847  array ( "name" => "Russia / ex Soviet Union",  "iso" => "ru", "conf" => "ringcadence = 800,3200\ndial = 425\nbusy = 425/350,0/350\nring = 425/800,0/3200\ncongestion = 425/350,0/350\ncallwaiting = 425/200,0/5000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\n"),
3848  array ( "name" => "Singapore",  "iso" => "sg", "conf" => "ringcadence = 400,200,400,2000\ndial = 425\nring = 425*24/400,0/200,425*24/400,0/2000 ; modulation should be 100%, not 90%\nbusy = 425/750,0/750\ncongestion = 425/250,0/250\ncallwaiting = 425*24/300,0/200,425*24/300,0/3200\nstutter = !425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425\ninfo = 950/330,1400/330,1800/330,0/1000 ; not currently in use acc. to reference\ndialrecall = 425*24/500,0/500,425/500,0/2500 ; unspecified in IDA reference, use repeating Holding Tone A,B\nrecord = 1400/500,0/15000 ; unspecified in IDA reference, use 0.5s tone every 15s\nnutone = 425/2500,0/500\nintrusion = 425/250,0/2000\nwarning = 425/624,0/4376 ; end of period tone, warning\nacceptance = 425/125,0/125\nholdinga = !425*24/500,!0/500 ; followed by holdingb\nholdingb = !425/500,!0/2500\n"),
3849  array ( "name" => "South Africa",  "iso" => "za", "conf" => "ringcadence = 400,200,400,2000\ndial = 400*33\nbusy = 400/500,0/500\nring = 400*33/400,0/200,400*33/400,0/2000\ncongestion = 400/250,0/250\ncallwaiting = 400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250\ndialrecall = 350+440\nrecord = 1400/500,0/10000\ninfo = 950/330,1400/330,1800/330,0/330\nstutter =!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33 \n"),
3850  array ( "name" => "Sweden",  "iso" => "se", "conf" => "ringcadence = 1000,5000\ndial = 425\nbusy = 425/250,0/250\nring = 425/1000,0/5000\ncongestion = 425/250,0/750\ncallwaiting = 425/200,0/500,425/200,0/9100\ndialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\nrecord = 1400/500,0/15000\ninfo = !950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0\nstutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425\n"),
3851  array ( "name" => "United Kingdom",  "iso" => "uk", "conf" => "ringcadence = 400,200,400,2000\ndial = 350+440\nspecialdial = 350+440/750,440/750\nbusy = 400/375,0/375\ncongestion = 400/400,0/350,400/225,0/525\nspecialcongestion = 400/200,1004/300\nunobtainable = 400\nring = 400+450/400,0/200,400+450/400,0/2000\ncallwaiting = 400/100,0/4000\nspecialcallwaiting = 400/250,0/250,400/250,0/250,400/250,0/5000\ncreditexpired = 400/125,0/125\nconfirm = 1400\nswitching = 400/200,0/400,400/2000,0/400\ninfo = 950/330,0/15,1400/330,0/15,1800/330,0/1000\nrecord = 1400/500,0/60000\nstutter = 350+440/750,440/750\n"),
3852  array ( "name" => "United States / North America",  "iso" => "us", "conf" => "ringcadence = 2000,4000\ndial = 350+440\nbusy = 480+620/500,0/500\nring = 440+480/2000,0/4000\ncongestion = 480+620/250,0/250\ncallwaiting = 440/300,0/10000\ndialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3853  array ( "name" => "United States Circa 1950/ North America",  "iso" => "us-old", "conf" => "ringcadence = 2000,4000\ndial = 600*120\nbusy = 500*100/500,0/500\nring = 420*40/2000,0/4000\ncongestion = 500*100/250,0/250\ncallwaiting = 440/300,0/10000\ndialrecall = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120\n"),
3854  array ( "name" => "Taiwan",  "iso" => "tw", "conf" => "ringcadence = 1000,4000\ndial = 350+440\nbusy = 480+620/500,0/500\nring = 440+480/1000,0/2000\ncongestion = 480+620/250,0/250\ncallwaiting = 350+440/250,0/250,350+440/250,0/3250\ndialrecall = 300/1500,0/500\nrecord = 1400/500,0/15000\ninfo = !950/330,!1400/330,!1800/330,0\nstutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440\n"),
3855  array ( "name" => "Venezuela / South America",  "iso" => "ve", "conf" => "; Tone definition source for ve found on\n; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf\nringcadence = 1000,4000\ndial = 425\nbusy = 425/500,0/500\nring = 425/1000,0/4000\ncongestion = 425/250,0/250\ncallwaiting = 400+450/300,0/6000\ndialrecall = 425\nrecord =  1400/500,0/15000\ninfo = !950/330,!1440/330,!1800/330,0/1000\n"),);
3856 }
3857
3858 function general_display_zones($curzone) {
3859   $zonelist = general_get_zonelist();
3860   echo "<select name='TONEZONE'>\n";
3861   foreach ($zonelist as $zone) {
3862     if ($zone['iso'] == $curzone)
3863       echo "<option selected value='{$zone['iso']}'>{$zone['name']}</option>\n";
3864     else 
3865       echo "<option value='{$zone['iso']}'>{$zone['name']}</option>\n";
3866   }
3867   echo "</select>";
3868  
3869 }
3870
3871 function general_generate_indications() {
3872   global $db;
3873   global $asterisk_conf;
3874
3875   $sql = "SELECT value FROM globals WHERE variable='TONEZONE'";
3876   $result = $db->getRow($sql,DB_FETCHMODE_ASSOC);
3877
3878   $filename = isset($asterisk_conf["astetcdir"]) && $asterisk_conf["astetcdir"] != '' ? rtrim($asterisk_conf["astetcdir"],DIRECTORY_SEPARATOR) : "/etc/asterisk";
3879   $filename .= "/indications.conf";
3880   $fd = fopen($filename, "w");
3881   fwrite($fd, "[general]\ncountry=".$result['value']."\n\n");
3882
3883   $zonelist = general_get_zonelist();
3884   foreach ($zonelist as $zone) {
3885     fwrite($fd, "[{$zone['iso']}]\n{$zone['conf']}\n\n");
3886   }
3887   fclose($fd);
3888 }
3889 /* end page.routing.php functions */
3890
3891
3892
3893 // init registered 'your' config load and config process functions
3894 function core_users_configpageinit($dispnum) {
3895   global $currentcomponent;
3896   global $amp_conf;
3897
3898   if ( $dispnum == 'users' || $dispnum == 'extensions' ) {
3899     // Setup option list we need
3900     $currentcomponent->addoptlistitem('recordoptions', 'Adhoc', _("On Demand"));
3901     $currentcomponent->addoptlistitem('recordoptions', 'Always', _("Always"));
3902     $currentcomponent->addoptlistitem('recordoptions', 'Never', _("Never"));
3903     $currentcomponent->setoptlistopts('recordoptions', 'sort', false);
3904
3905     $currentcomponent->addoptlistitem('faxdetecttype', '0', _("None"));
3906     $currentcomponent->addoptlistitem('faxdetecttype', '1', 'Zaptel');
3907     $currentcomponent->addoptlistitem('faxdetecttype', '2', 'NVFax');
3908     $currentcomponent->setoptlistopts('faxdetecttype', 'sort', false);
3909
3910     $currentcomponent->addoptlistitem('privyn', '0', _("No"));
3911     $currentcomponent->addoptlistitem('privyn', '1', _("Yes"));
3912     $currentcomponent->setoptlistopts('privyn', 'sort', false);
3913
3914     $currentcomponent->addoptlistitem('callwaiting', 'enabled', _("Enable"));
3915     $currentcomponent->addoptlistitem('callwaiting', 'disabled', _("Disable"));
3916     $currentcomponent->setoptlistopts('callwaiting', 'sort', false);
3917
3918     $currentcomponent->addoptlistitem('ringtime', '0', 'Default');
3919     for ($i=1; $i <= 120; $i++) {
3920       $currentcomponent->addoptlistitem('ringtime', "$i", "$i");
3921     }
3922     $currentcomponent->setoptlistopts('ringtime', 'sort', false);
3923
3924     $currentcomponent->addoptlistitem('faxdestoptions', 'default', _("FreePBX default"));
3925     $currentcomponent->addoptlistitem('faxdestoptions', 'disabled', _("disabled"));
3926     $currentcomponent->addoptlistitem('faxdestoptions', 'system', _("system"));
3927     $currentcomponent->setoptlistopts('faxdestoptions', 'sort', false);
3928
3929     if (function_exists('music_list')) {
3930         $tresults = music_list($amp_conf['ASTVARLIBDIR']."/mohmp3");
3931         if (isset($tresults[0])) {
3932       foreach ($tresults as $tresult) {
3933           $currentcomponent->addoptlistitem('mohclass', $tresult, $tresult);
3934       }
3935         $currentcomponent->setoptlistopts('mohclass', 'sort', false);
3936         }
3937     }
3938
3939     //get unique devices to finishoff faxdestoptions list
3940     $devices = core_devices_list();
3941     if (isset($devices)) {
3942       foreach ($devices as $device) {
3943         $currentcomponent->addoptlistitem('faxdestoptions', $device[0], "$device[1] <$device[0]>");
3944       }
3945     }
3946
3947     // Add the 'proces' functions
3948     $currentcomponent->addguifunc('core_users_configpageload');
3949     // Ensure users is called in middle order ($sortorder = 5), this is to allow
3950     // other modules to call stuff before / after the processing of users if needed
3951     // e.g. Voicemail module needs to create mailbox BEFORE the users as the mailbox
3952     // context is needed by the add users function
3953     $currentcomponent->addprocessfunc('core_users_configprocess', 5);     
3954   }
3955 }
3956
3957 function core_users_configpageload() {
3958   global $currentcomponent;
3959   global $amp_conf;
3960
3961   // Ensure variables possibly extracted later exist
3962   $name = $directdid = $didalert = $outboundcid = $answer = null;
3963   $record_in = $record_out = $faxexten = $faxemail = $mohclass = $sipname = $cid_masquerade = null;
3964
3965   // Init vars from $_REQUEST[]
3966   $display = isset($_REQUEST['display'])?$_REQUEST['display']:null;;
3967   $action = isset($_REQUEST['action'])?$_REQUEST['action']:null;
3968   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
3969   $tech_hardware = isset($_REQUEST['tech_hardware'])?$_REQUEST['tech_hardware']:null;
3970
3971   if ( $action == 'del' ) { // Deleted
3972
3973     $currentcomponent->addguielem('_top', new gui_subheading('del', $extdisplay.' '._("deleted"), false));
3974
3975   } elseif ( $display == 'extensions' && ($extdisplay == '' && $tech_hardware == '') ) { // Adding
3976
3977     // do nothing as you want the Devices to handle this bit
3978
3979   } else {
3980
3981     $delURL = $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'].'&action=del';
3982  
3983     if ( is_string($extdisplay) ) {
3984
3985       if (!isset($GLOBALS['abort']) || $GLOBALS['abort'] !== true) {
3986         $extenInfo=core_users_get($extdisplay);
3987         extract($extenInfo);
3988       }
3989       if (isset($deviceInfo) && is_array($deviceInfo))
3990         extract($deviceInfo);
3991  
3992       if ( $display == 'extensions' ) {
3993         $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Extension").": $extdisplay", false), 0);
3994         if (!isset($GLOBALS['abort']) || $GLOBALS['abort'] !== true) {
3995           $currentcomponent->addguielem('_top', new gui_link('del', _("Delete Extension")." $extdisplay", $delURL, true, false), 0);
3996
3997           $usage_list = framework_display_destination_usage(core_getdest($extdisplay));
3998           if (!empty($usage_list)) {
3999             $currentcomponent->addguielem('_top', new gui_link_label('dests', $usage_list['text'], $usage_list['tooltip'], true), 0);
4000           }
4001         }
4002       } else {
4003         $currentcomponent->addguielem('_top', new gui_pageheading('title', _("User").": $extdisplay", false), 0);
4004         if (!isset($GLOBALS['abort']) || $GLOBALS['abort'] !== true) {
4005<