| 1 |
<?php |
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
class Dir{ |
|---|
| 6 |
|
|---|
| 7 |
var $agi; |
|---|
| 8 |
|
|---|
| 9 |
var $agivar; |
|---|
| 10 |
|
|---|
| 11 |
var $ami; |
|---|
| 12 |
|
|---|
| 13 |
var $db; |
|---|
| 14 |
|
|---|
| 15 |
var $dir; |
|---|
| 16 |
|
|---|
| 17 |
var $directory; |
|---|
| 18 |
|
|---|
| 19 |
var $searchstring; |
|---|
| 20 |
|
|---|
| 21 |
var $vmbasedir=''; |
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
function Dir(){ |
|---|
| 25 |
$this->__construct(); |
|---|
| 26 |
} |
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
function __construct(){ |
|---|
| 30 |
$this->agi=$this->__construct_agi(); |
|---|
| 31 |
|
|---|
| 32 |
$this->db=$this->__construct_db(); |
|---|
| 33 |
|
|---|
| 34 |
$this->directory=$this->agivar['dir']; |
|---|
| 35 |
$this->dir=$this->__construct_dir_opts(); |
|---|
| 36 |
} |
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
function __construct_agi(){ |
|---|
| 40 |
require_once('phpagi.php'); |
|---|
| 41 |
$agi=new AGI(); |
|---|
| 42 |
foreach($agi->request as $key => $value){ |
|---|
| 43 |
if(substr($key,0,4)=='agi_'){ |
|---|
| 44 |
$opts[substr($key,4)]=$value; |
|---|
| 45 |
} |
|---|
| 46 |
} |
|---|
| 47 |
|
|---|
| 48 |
foreach($opts as $key => $value){ |
|---|
| 49 |
if(substr($key,0,4)=='arg_'){ |
|---|
| 50 |
$expld=explode('=',$value); |
|---|
| 51 |
$opts[$expld[0]]=$expld[1]; |
|---|
| 52 |
unset($opts[$key]); |
|---|
| 53 |
} |
|---|
| 54 |
} |
|---|
| 55 |
|
|---|
| 56 |
array_shift($_SERVER['argv']); |
|---|
| 57 |
foreach($_SERVER['argv'] as $arg){ |
|---|
| 58 |
$arg=explode('=',$arg); |
|---|
| 59 |
|
|---|
| 60 |
if(substr($arg['0'],0,2) == '--'){$arg['0']=substr($arg['0'],2);} |
|---|
| 61 |
$opts[$arg['0']]=isset($arg['1'])?$arg['1']:null; |
|---|
| 62 |
} |
|---|
| 63 |
$this->agivar=$opts; |
|---|
| 64 |
return $agi; |
|---|
| 65 |
} |
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
function __construct_ami(){ |
|---|
| 69 |
require_once('phpagi-asmanager.php'); |
|---|
| 70 |
$ami=new AGI_AsteriskManager(); |
|---|
| 71 |
return $ami; |
|---|
| 72 |
} |
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
// TODO: hardcoded mysql, deal with sqlite... |
|---|
| 76 |
// |
|---|
| 77 |
function __construct_db(){ |
|---|
| 78 |
require_once("DB.php"); |
|---|
| 79 |
$dsn=array( |
|---|
| 80 |
'phptype' => $this->agi_get_var('AMPDBENGINE'), |
|---|
| 81 |
'hostspec' => $this->agi_get_var('AMPDBHOST'), |
|---|
| 82 |
'database' => $this->agi_get_var('AMPDBNAME'), |
|---|
| 83 |
'username' => $this->agi_get_var('AMPDBUSER'), |
|---|
| 84 |
'password' => $this->agi_get_var('AMPDBPASS'), |
|---|
| 85 |
); |
|---|
| 86 |
$db=DB::connect($dsn); |
|---|
| 87 |
return $db; |
|---|
| 88 |
} |
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
// TODO: handle getRow failures |
|---|
| 92 |
function __construct_dir_opts(){ |
|---|
| 93 |
$sql='SELECT * FROM directory_details WHERE ID = ?'; |
|---|
| 94 |
$row=$this->db->getRow($sql,array($this->directory),DB_FETCHMODE_ASSOC); |
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
//If any non-defaults (non-zero id) then lookup files |
|---|
| 98 |
// |
|---|
| 99 |
if ($row['announcement'] || $row['repeat_recording'] || $row['invalid_recording']) { |
|---|
| 100 |
$sql='SELECT id, filename from recordings where id in ('.$row['announcement'].','.$row['repeat_recording'].','.$row['invalid_recording'].')'; |
|---|
| 101 |
$res=$this->db->getAll($sql,DB_FETCHMODE_ASSOC); |
|---|
| 102 |
if(DB::IsError($res)) { |
|---|
| 103 |
dbug("FATAL: got error from getAll query",1); |
|---|
| 104 |
dbug($res->getDebugInfo()); |
|---|
| 105 |
} |
|---|
| 106 |
$rec_file = array(); |
|---|
| 107 |
foreach ($res as $entry) { |
|---|
| 108 |
|
|---|
| 109 |
$rec_file[$entry['id']] = $entry['filename']; |
|---|
| 110 |
} |
|---|
| 111 |
unset($res); |
|---|
| 112 |
} |
|---|
| 113 |
$row['announcement'] = $row['announcement']&&isset($rec_file[$row['announcement']])?$rec_file[$row['announcement']]:'cdir-please-enter-first-three'; |
|---|
| 114 |
$row['repeat_recording'] = $row['repeat_recording']&&isset($rec_file[$row['repeat_recording']])?$rec_file[$row['repeat_recording']]:'cdir-sorry-no-entries'; |
|---|
| 115 |
$row['invalid_recording'] = $row['invalid_recording']&&isset($rec_file[$row['invalid_recording']])?$rec_file[$row['invalid_recording']]:'cdir-transferring-further-assistance'; |
|---|
| 116 |
return $row; |
|---|
| 117 |
} |
|---|
| 118 |
|
|---|
| 119 |
|
|---|
| 120 |
function agi_get_var($var){ |
|---|
| 121 |
global $agi_cache; |
|---|
| 122 |
if (isset($agi_cache[$var])) { |
|---|
| 123 |
return $agi_cache[$var]; |
|---|
| 124 |
} |
|---|
| 125 |
$ret=$this->agi->get_variable($var); |
|---|
| 126 |
if($ret['result']==1){ |
|---|
| 127 |
$result=$ret['data']; |
|---|
| 128 |
$agi_cache[$var] = $result; |
|---|
| 129 |
return $result; |
|---|
| 130 |
}else{ |
|---|
| 131 |
return ''; |
|---|
| 132 |
} |
|---|
| 133 |
} |
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
// TODO: make it so you can pass in an array: |
|---|
| 137 |
// |
|---|
| 138 |
function getKeypress($filename, $pressables='', $timeout=2000){ |
|---|
| 139 |
if (!is_array($filename)) { |
|---|
| 140 |
$filename = array($filename); |
|---|
| 141 |
} |
|---|
| 142 |
foreach ($filename as $chunk) { |
|---|
| 143 |
$ret=is_int($chunk)?$this->agi->say_number($chunk,$pressables):$this->agi->stream_file($chunk,$pressables); |
|---|
| 144 |
if(!empty($ret['result'])) {break;} |
|---|
| 145 |
} |
|---|
| 146 |
if(empty($ret['result'])){ |
|---|
| 147 |
$ret=$this->agi->wait_for_digit($timeout); |
|---|
| 148 |
} |
|---|
| 149 |
switch ($ret['result']) { |
|---|
| 150 |
case 0: |
|---|
| 151 |
return null; |
|---|
| 152 |
case -1: |
|---|
| 153 |
return false; |
|---|
| 154 |
default: |
|---|
| 155 |
return chr($ret['result']); |
|---|
| 156 |
} |
|---|
| 157 |
} |
|---|
| 158 |
|
|---|
| 159 |
function readContact($con, $keys='#'){ |
|---|
| 160 |
switch($con['audio']){ |
|---|
| 161 |
case 'vm': |
|---|
| 162 |
$vm_dir = $this->agi->database_get('AMPUSER',$con['dial'].'/voicemail'); |
|---|
| 163 |
$vm_dir = $vm_dir['data']; |
|---|
| 164 |
dbug('got directory ' . $vm_dir . ' for user ' . $con['dial']); |
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 |
if ($vm_dir && $vm_dir != 'novm') { |
|---|
| 168 |
if (!$this->vmbasedir) { |
|---|
| 169 |
$this->vmbasedir = $this->agi_get_var('ASTSPOOLDIR').'/voicemail/'; |
|---|
| 170 |
} |
|---|
| 171 |
|
|---|
| 172 |
$dir=scandir($this->vmbasedir.$vm_dir.'/'.$con['dial']); |
|---|
| 173 |
foreach($dir as $file){ |
|---|
| 174 |
dbug("looking for vm file $file using: ".basename($file),6); |
|---|
| 175 |
if(substr($file,0,5) == 'greet' && is_file($this->vmbasedir.$vm_dir.'/'.$con['dial'].'/'.$file)){ |
|---|
| 176 |
$ret=$this->agi->stream_file($this->vmbasedir.$vm_dir.'/'.$con['dial'].'/greet',$keys); |
|---|
| 177 |
if ($ret['result']) { |
|---|
| 178 |
$ret['result']=chr($ret['result']); |
|---|
| 179 |
} |
|---|
| 180 |
break 2; |
|---|
| 181 |
} |
|---|
| 182 |
} |
|---|
| 183 |
} |
|---|
| 184 |
|
|---|
| 185 |
case 'spell': |
|---|
| 186 |
foreach(str_split(strtolower($con['name']),1) as $char){ |
|---|
| 187 |
dbug('saying '.$char.' from string '.strtolower($con['name'])); |
|---|
| 188 |
switch(true){ |
|---|
| 189 |
case ctype_alpha($char): |
|---|
| 190 |
$ret = $this->agi->evaluate('SAY ALPHA '.$char.' '.$keys); |
|---|
| 191 |
dbug("returned from SAY ALPHA with code/result {$ret['code']}/{$ret['result']}",6); |
|---|
| 192 |
break; |
|---|
| 193 |
case ctype_digit($char): |
|---|
| 194 |
$ret = $this->agi->say_digits($char, $keys); |
|---|
| 195 |
break; |
|---|
| 196 |
case ctype_space($char): |
|---|
| 197 |
$ret = $this->agi->wait_for_digit(750); |
|---|
| 198 |
break; |
|---|
| 199 |
} |
|---|
| 200 |
if(trim($ret['result'])) { |
|---|
| 201 |
$ret['result'] = chr($ret['result']); |
|---|
| 202 |
break; |
|---|
| 203 |
} |
|---|
| 204 |
} |
|---|
| 205 |
break; |
|---|
| 206 |
|
|---|
| 207 |
//we dont call the flite app cirectly as it still uses | as a parameter separator |
|---|
| 208 |
case 'tts': |
|---|
| 209 |
$tmp = $this->agi_get_var('ASTSPOOLDIR') . '/tmp/directory-tts-' . time() . rand(100, 999); |
|---|
| 210 |
system('flite -t "' . $con['name'] .'" -o ' . $tmp . '.wav', $exit_code); |
|---|
| 211 |
if ($exit_code === 0) { |
|---|
| 212 |
$ret = $this->agi->stream_file($tmp, $keys); |
|---|
| 213 |
if ($ret['result']) { |
|---|
| 214 |
$ret['result'] = chr($ret['result']); |
|---|
| 215 |
} |
|---|
| 216 |
unlink($tmp . '.wav'); |
|---|
| 217 |
} else { |
|---|
| 218 |
$ret = array('result' => ''); |
|---|
| 219 |
} |
|---|
| 220 |
|
|---|
| 221 |
break; |
|---|
| 222 |
default: |
|---|
| 223 |
if(is_numeric($con['audio'])){ |
|---|
| 224 |
$sql='SELECT filename from recordings where id = ?'; |
|---|
| 225 |
$rec=$this->db->getOne($sql, array($con['audio'])); |
|---|
| 226 |
dbug("got record id: {$con['audio']} file(s): $rec"); |
|---|
| 227 |
if($rec){ |
|---|
| 228 |
$rec=explode('&',$rec); |
|---|
| 229 |
foreach($rec as $r){ |
|---|
| 230 |
$ret=$this->agi->stream_file($r,$keys); |
|---|
| 231 |
if(trim($ret['result'])){$ret['result']=chr($ret['result']);break;} |
|---|
| 232 |
} |
|---|
| 233 |
} else { |
|---|
| 234 |
|
|---|
| 235 |
dbug("ERROR: unknown/undefined sound file"); |
|---|
| 236 |
} |
|---|
| 237 |
} |
|---|
| 238 |
break; |
|---|
| 239 |
} |
|---|
| 240 |
return $ret; |
|---|
| 241 |
} |
|---|
| 242 |
|
|---|
| 243 |
function search($key,$count=0){ |
|---|
| 244 |
if($key == ''){return false;} |
|---|
| 245 |
|
|---|
| 246 |
if(strstr($key,'0') !== false) { |
|---|
| 247 |
dbug("user pressed 0 - bailing out"); |
|---|
| 248 |
$this->bail(); |
|---|
| 249 |
} |
|---|
| 250 |
|
|---|
| 251 |
|
|---|
| 252 |
$num= array('1','2','3','4','5','6','7','8','9','0','#'); |
|---|
| 253 |
$alph=array("[ \s@,-\!/+=\.']",'[abcABC]','[defDEF]','[ghiGHI]','[jklJKL]','[mnoMNO]','[pqrsPQRS]','[tuvTUV]','[wxyzWXYZ]','',''); |
|---|
| 254 |
$this->searchstring=$this->db->escapeSimple(str_replace($num,$alph,$key)); |
|---|
| 255 |
dbug("search string for regex: {$this->searchstring}"); |
|---|
| 256 |
|
|---|
| 257 |
|
|---|
| 258 |
|
|---|
| 259 |
$vtable = '(SELECT DISTINCT a.audio, IF(a.name != "",a.name,b.name) name, IF(a.dial != "",a.dial,b.extension) dial FROM directory_entries a LEFT JOIN users b ON a.foreign_id = b.extension WHERE id = "'.$this->directory.'") v'; |
|---|
| 260 |
if($count==1){ |
|---|
| 261 |
$sql="SELECT COUNT(*) FROM $vtable WHERE name REGEXP \"(^| ){$this->searchstring}\""; |
|---|
| 262 |
$res=$this->db->getOne($sql); |
|---|
| 263 |
if(DB::IsError($res)) { |
|---|
| 264 |
dbug("FATAL: got error from COUNT(*) query"); |
|---|
| 265 |
dbug($res->getDebugInfo()); |
|---|
| 266 |
} |
|---|
| 267 |
dbug("Found $res possible matches from $key"); |
|---|
| 268 |
}else{ |
|---|
| 269 |
$sql="SELECT * FROM $vtable WHERE name REGEXP \"(^| ){$this->searchstring}\""; |
|---|
| 270 |
$res=$this->db->getAll($sql,DB_FETCHMODE_ASSOC); |
|---|
| 271 |
if(DB::IsError($res)) { |
|---|
| 272 |
dbug("FATAL: got error from getAll query"); |
|---|
| 273 |
dbug($res->getDebugInfo()); |
|---|
| 274 |
} else { |
|---|
| 275 |
dbug("Found the following matches:"); |
|---|
| 276 |
foreach ($res as $ent) { |
|---|
| 277 |
dbug("name: {$ent['name']}, audio: {$ent['audio']}, dial: {$ent['dial']}"); |
|---|
| 278 |
} |
|---|
| 279 |
} |
|---|
| 280 |
} |
|---|
| 281 |
return $res; |
|---|
| 282 |
} |
|---|
| 283 |
|
|---|
| 284 |
function bail() { |
|---|
| 285 |
|
|---|
| 286 |
// |
|---|
| 287 |
dbug("User pressed zero, passing back recording of {$this->dir['invalid_recording']}"); |
|---|
| 288 |
$this->agi->set_variable('DIR_INVALID_RECORDING',$this->dir['invalid_recording']); |
|---|
| 289 |
if($this->agi_get_var('IVR_CONTEXT')){ |
|---|
| 290 |
$this->agi->set_extension('retivr'); |
|---|
| 291 |
}else{ |
|---|
| 292 |
$dest = explode(',',$this->dir['invalid_destination']); |
|---|
| 293 |
$this->agi->set_variable('DIR_INVALID_CONTEXT',$dest['0']); |
|---|
| 294 |
$this->agi->set_variable('DIR_INVALID_EXTEN',$dest['1']); |
|---|
| 295 |
$this->agi->set_variable('DIR_INVALID_PRI',$dest['2']); |
|---|
| 296 |
$this->agi->set_extension('invalid'); |
|---|
| 297 |
} |
|---|
| 298 |
$this->agi->set_priority('1'); |
|---|
| 299 |
exit; |
|---|
| 300 |
} |
|---|
| 301 |
} |
|---|
| 302 |
|
|---|
| 303 |
?> |
|---|
| 304 |
|
|---|