function SendMsg($msg) { global $ourcallid; global $partycallid; $m = new Yate($msg); $m->id = ""; $m->params["id"] = $ourcallid; $m->params["peerid"] = $partycallid; $m->params["targetid"] = $partycallid; $m->params["reason"] = "queued"; $m->params["cdrcreate"] = "false"; $m->Dispatch(); }
function query($query) { global $conn, $query_on, $max_resets_conn; global $dsn; $resets = 0; while (true) { if ($conn) { $res = $conn->query($query); } /* if(!$res) { while(true) { if($resets >= ($max_resets_conn-1)) { Yate::Output("Could not execute: $query\n"); return null; } $resets++; if (!$conn) { $conn = DB::connect($dsn, true); break; sleep(1); }else $resets = $max_resets_conn; } }else */ break; } if ($query_on) { Yate::Output("Executed: {$query}" . "\n"); } return $res; }
function onDisconnect(&$ev, $reason) { global $ourcallid; Yate::Output("Channel {$ourcallid} was disconnected, reason: '{$reason}'"); // Sample action: redirect to info tone if user is busy if ($reason == "busy") { $m = new Yate("call.execute"); $m->id = $ev->id; $m->params["id"] = $ourcallid; $m->params["callto"] = "tone/info"; $m->Dispatch(); // Also send progressing so the tone goes through in early media $m = new Yate("call.progress"); $m->params["targetid"] = $ourcallid; $m->Dispatch(); return true; } return false; }
function secondCall($route, $ev) { global $partycallid; global $real_called; global $billid; global $callFrom; // global $place; //$real_caller = '79297333104'; $m = new Yate("chan.masquerade"); $m->params = $ev->params; $m->SetParam("message", "call.execute"); $m->SetParam("callto", $route); //$m->SetParam("caller", $real_caller); $m->SetParam("called", $real_called); $m->SetParam("id", $partycallid); $m->SetParam("billid", $billid); $m->SetParam("call_from", $callFrom); $m->Dispatch(); }
function vmGetMessageFiles($mailbox, &$files) { $dir = vmGetVoicemailDir($mailbox); if (is_dir($dir) && ($d = @opendir($dir))) { $nf = array(); $of = array(); while (($f = readdir($d)) !== false) { if (substr($f, 0, 4) == "nvm-") { Yate::Debug("found new file '{$f}'"); $nf[] = $f; } else { if (substr($f, 0, 3) == "vm-") { Yate::Debug("found old file '{$f}'"); $of[] = $f; } } } closedir($d); $files = array_merge($nf, $of); return true; } return false; }
function doRoute(&$ev) { global $prefix, $calls; $called = $ev->GetValue("called"); // Check if called number starts with prefix $len = strlen($prefix); if (substr($called, 0, $len) != $prefix) { return; } // Get rid of prefix and search the active calls $called = substr($called, $len); if ($called == "") { return; } // Have to search, cannot use number as array key because it's not unique $chan = array_search($called, $calls); Yate::Debug("For picking up '{$called}' found channel '{$chan}'"); if ($chan == "") { return; } // Found! Route to it and signal success $ev->retval = "pickup/{$chan}"; $ev->handled = true; }
function Update($ev, $id) { $st = ""; $dir = ""; switch ($ev->GetValue("operation")) { case "initialize": $st = "trying"; break; case "finalize": $st = "terminated"; break; default: switch ($ev->GetValue("status")) { case "connected": case "answered": $st = "confirmed"; break; case "incoming": case "outgoing": case "calling": case "ringing": case "progressing": $st = "early"; break; case "redirected": $st = "rejected"; break; case "destroyed": $st = "terminated"; break; } } if ($st != "") { // directions are reversed because we are talking about the remote end switch ($ev->GetValue("direction")) { case "incoming": $dir = "initiator"; break; case "outgoing": $dir = "recipient"; break; } if ($dir != "") { $dir = " direction=\"{$dir}\""; } } else { $id = false; } Yate::Debug("Dialog updated, st: '{$st}' id: '{$id}'"); $this->body = "<?xml version=\"1.0\"?>\r\n"; $this->body .= "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"" . $this->version . "\" entity=\"" . $this->uri . "\" notify-state=\"full\">\r\n"; if ($id) { $uri = ereg_replace("^.*<([^<>]+)>.*\$", "\\1", $this->uri); $tag = $this->match; $tag = " call-id=\"{$id}\" local-tag=\"{$tag}\" remote-tag=\"{$tag}\""; $this->body .= " <dialog id=\"{$id}\"{$tag}{$dir}>\r\n"; $this->body .= " <state>{$st}</state>\r\n"; $this->body .= " <remote><target uri=\"{$uri}\"/></remote>\r\n"; $this->body .= " </dialog>\r\n"; } $this->body .= "</dialog-info>\r\n"; $this->version++; $this->pending = true; if ($id) { $this->Flush(); } }
// caller hung up while in queue exit; } /* This is extremely important. We MUST let messages return, handled or not */ if ($ev) { $ev->Acknowledge(); } break; case "answer": Yate::Debug("PHP Answered: " . $ev->name . " id: " . $ev->id); if (!$ev->handled) { if ($ev->name == "call.execute") { // call leg to operator didn't even start Yate::Output("Failed to start queue '{$queue}' call leg to: " . $ev->GetValue("callto")); $m = new Yate("chan.hangup"); $m->id = ""; $m->params["notify"] = $partycallid; $m->params["queue"] = $queue; $m->params["cdrtrack"] = "false"; $m->Dispatch(); } else { if ($ev->name == "chan.locate") { // caller hung up before we installed the hangup handler Yate::Output("Call {$partycallid} from '{$caller}' exited early from '{$queue}'"); Yate::SetLocal("reason", "nocall"); exit; } } } break;
function setState($newstate) { global $ourcallid; global $state; global $vm_base; global $mailbox; global $user; global $file; // are we exiting? if ($state == "") { return; } debug("setState('{$newstate}') state: {$state}"); if ($newstate == $state) { return; } switch ($newstate) { case "novmail": $m = new Yate("chan.attach"); $m->params["source"] = "wave/play/{$vm_base}/novmail.au"; $m->params["notify"] = $ourcallid; $m->Dispatch(); break; case "greeting": $m = new Yate("chan.attach"); if (is_file("{$user}/greeting.au")) { $m->params["source"] = "wave/play/{$user}/greeting.au"; } else { $m->params["source"] = "wave/play/{$vm_base}/nogreeting.au"; } $m->params["notify"] = $ourcallid; $m->Dispatch(); break; case "beep": $m = new Yate("chan.attach"); $m->params["source"] = "wave/play/{$vm_base}/beep.au"; $m->params["notify"] = $ourcallid; $m->Dispatch(); break; case "record": $m = new Yate("chan.attach"); $m->params["source"] = "wave/play/-"; $m->params["consumer"] = "wave/record/{$user}/{$file}"; $m->params["maxlen"] = 160000; $m->params["notify"] = $ourcallid; $m->Dispatch(); $m = new Yate("user.update"); $m->id = ""; $m->params["user"] = $mailbox; $m->Dispatch(); break; case "goodbye": $m = new Yate("chan.attach"); $m->params["source"] = "tone/congestion"; $m->params["consumer"] = "wave/record/-"; $m->params["maxlen"] = 32000; $m->params["notify"] = $ourcallid; $m->Dispatch(); break; } $state = $newstate; }
/** * This static function initializes globals in the PHP Yate Channel Module. * It should be called before any other method. * It will call Yate::Init internally. * @param $prefix (optional) Prefix used for the unique channel identifier * @param $async (optional) True if asynchronous, polled mode is desired * @param $addr Hostname to connect to or UNIX socket path * @param $port TCP port to connect to, zero to use UNIX sockets */ static function Init($prefix = "extchan", $async = false, $addr = "", $port = 0) { global $chan_instance; Yate::Init($async, $addr, $port, "channel"); $chan_instance = new YateChan($prefix); YateChan::RunEvents(); if ($chan_instance->exiting) { return; } Yate::Install("chan.dtmf"); Yate::Install("chan.notify"); }
function gotDTMF($text) { global $ourcallid; global $partycallid; global $state; Yate::Output("gotDTMF('" . $text . "') state: " . $state); switch ($text) { case "1": setState("record"); break; case "2": setState("play"); break; case "3": setState(""); break; case "#": setState("prompt"); break; } }
function onCommand($l, &$retval) { global $hosts; global $ban_failures; global $cmd_unban; if ($l == "banbrutes") { $gray = 0; $banned = 0; foreach ($hosts as &$host) { if ($host->banned()) { $banned++; } else { $gray++; } } $retval = "failures={$ban_failures},banned={$banned},gray={$gray}\r\n"; return true; } else { if ($l == "banbrutes list") { $retval = ""; $now = time(); foreach ($hosts as $addr => &$host) { if ($retval != "") { $retval .= ","; } if ($host->banned()) { $t = $host->when - $now; $retval .= "{$addr}=banned:{$t}s"; } else { $retval .= "{$addr}=gray:" . $host->fail; } } $retval .= "\r\n"; return true; } else { if (strpos($l, "banbrutes unban ") === 0) { $addr = substr($l, 16); if (isset($hosts[$addr])) { if ($hosts[$addr]->banned()) { $cmd = eval('return "' . $cmd_unban . '";'); Yate::Output("banbrutes: {$cmd}"); shell_exec($cmd); unset($hosts[$addr]); $retval = "Unbanned: {$addr}\r\n"; } else { unset($hosts[$addr]); $retval = "Removed from gray list: {$addr}\r\n"; } } else { $retval = "Not banned: {$addr}\r\n"; } return true; } else { if (strpos($l, "banbrutes failures ") === 0) { $fail = 1 * substr($l, 19); if ($fail > 1 && $fail <= 1000) { $ban_failures = $fail; return true; } } else { if (strpos($l, "banbrutes debug ") === 0) { $dbg = substr($l, 16); switch ($dbg) { case "true": case "yes": case "on": Yate::Debug(true); return true; case "false": case "no": case "off": Yate::Debug(false); return true; } } } } } } return false; }
function endRoute($callto, $ok, $err, $params) { global $partycallid; global $collect; global $final; global $queue; global $routeOnly; global $state; if ($ok && $callto != "-" && $callto != "error") { Yate::Output("Overlapped got route: '{$callto}' for '{$collect}'"); $m = new Yate("chan.masquerade"); $m->params = $params; $m->params["message"] = "call.execute"; $m->params["complete_minimal"] = true; $m->params["id"] = $partycallid; $m->params["callto"] = $callto; $m->Dispatch(); if (strlen($queue)) { // Masquerade the remaining digits // TODO: wait for call.execute to be processed to do that? $d = new Yate("chan.masquerade"); $d->params["message"] = "chan.dtmf"; $d->params["id"] = $partycallid; $d->params["tone"] = $queue; $d->Dispatch(); } return; } if ($final) { Yate::Output("Overlapped got final error '{$err}' for '{$collect}'"); Yate::SetLocal("reason", $err); setState(""); } else { if ($err != "incomplete") { Yate::Output("Overlapped got error '{$err}' for '{$collect}'"); Yate::SetLocal("reason", $err); setState(""); $final = true; } else { Yate::Debug("Overlapped still incomplete: '{$collect}'"); if ($routeOnly) { setState("noroute"); } else { // Don't use setState: we don't want to change the prompt $state = "prompt"; } // Check if got some other digits if ($queue != "") { gotDTMF(""); } } } }
function callAnswered() { global $ourcallid; global $prompt; global $done; Yate::Debug("callAnswered"); if ($prompt != "") { $m = new Yate("chan.attach"); $m->id = ""; $m->SetParam("id", $ourcallid); $m->SetParam("source", $prompt); $m->SetParam("single", true); $m->SetParam("notify", $ourcallid); $m->Dispatch(); } else { $done = true; } }
function setState($newstate) { global $ourcallid; global $partycallid; global $state; global $uploaded_prompts; global $keys; global $wait_time; global $caller; global $hold_keys; global $destination; global $called; global $ev; // are we exiting? if ($state == "") { return; } debug("setState('{$newstate}') state: {$state}"); $state = $newstate; // always obey a return to prompt switch ($newstate) { case "greeting": // check what prompt to use for this time of day //$query = "select prompts.prompt_id, prompts.file as prompt from time_frames, prompts /*where numeric_day=extract(dow from now()) and cast(start_hour as integer)<=extract(HOUR FROM now()) AND cast(end_hour as integer)>extract(HOUR FROM now()) and time_frames.prompt_id=prompts.prompt_id*/ UNION select prompt_id, file as prompt from prompts where status='online'"; $status = 'offline'; $query = "select prompt_id, day, start_hour, end_hour, numeric_day FROM time_frames"; $res = query_to_array($query); if (count($res)) { $day_week = date('w'); $hour = date('H') * 1; debug("Current week index '{$day_week}' : hour '{$hour}'"); //$day_week = 2; //$hour = 12; $status = 'offline'; foreach ($res as $row) { if ($row["numeric_day"] == $day_week && $row["start_hour"] <= $hour && $hour < $row["end_hour"]) { $hold_keys = ''; // Reset default key $status = 'online'; } } } $query = "select prompts.prompt_id, prompts.file as prompt from prompts where status='{$status}'"; $res = query_to_array($query); debug('greeting:' . format_array($res)); if (!count($res)) { debug("Auto-Attendant is not configured!!"); setState("goodbye"); return; } $prompt_id = $res[0]["prompt_id"]; $prompt = $res[0]["prompt"]; // here we must have ".au" //$prompt = str_ireplace(".mp3", ".slin", $prompt); $query = "SELECT keys.key, destination FROM `keys` WHERE prompt_id={$prompt_id}"; $keys = query_to_array($query); debug('keys:' . format_array($keys)); $m = new Yate("chan.attach"); // ------------------------ /* prompt based on called */ $prompt_file = "{$uploaded_prompts}/auto_attendant/{$prompt}"; //debug('source[called]:<' . $prompt_file.".".$called.">"); if (file_exists($prompt_file . "." . $called)) { $prompt_file = $prompt_file . "." . $called; } $m->params["source"] = "wave/play/{$prompt_file}"; debug('source:' . "wave/play/{$prompt_file}"); // ------------------------ /* $m->params["source"] = "wave/play/$uploaded_prompts/auto_attendant/$prompt"; debug('source:' . "wave/play/$uploaded_prompts/auto_attendant/$prompt"); */ $m->params["notify"] = $ourcallid; $m->Dispatch(); break; case "prolong_greeting": $m = new Yate("chan.attach"); $m->params["consumer"] = "wave/record/-"; $m->params["notify"] = $ourcallid; $m->params["maxlen"] = $wait_time * 10000; $m->Dispatch(); break; case "goodbye": $m = new Yate("chan.attach"); $m->params["source"] = "tone/congestion"; $m->params["consumer"] = "wave/record/-"; $m->params["maxlen"] = 32000; $m->params["notify"] = $ourcallid; $m->Dispatch(); break; case "call.route": $to_call = null; debug('$keys = ' . $keys); for ($i = 0; $i < count($keys); $i++) { if ($keys[$i]["key"] == $hold_keys) { $to_call = $keys[$i]["destination"]; //$hold_keys = null; break; } } if ($hold_keys == '') { debug('$called = ' . $called); $query = "SELECT (CASE WHEN extension_id IS NOT NULL THEN (SELECT extension FROM extensions WHERE extensions.extension_id=dids.extension_id) ELSE (SELECT extension FROM groups WHERE groups.group_id=dids.group_id) END) as called FROM dids WHERE number='{$called}'"; $res = query_to_array($query); if (!count($res) || !strlen($res[0]["called"])) { // this should never happen setState("goodbye"); return; } $to_call = $res[0]["called"]; } if (!$to_call) { $to_call = $hold_keys; } $m = new Yate("call.route"); $m->params["caller"] = $caller; $m->params["called"] = $to_call; $m->params["id"] = $ourcallid; $m->params["already-auth"] = "yes"; $m->params["call_type"] = "from outside"; $m->Dispatch(); break; case "send_call": $m = new Yate("chan.masquerade"); $m->params = $ev->params; $m->params["message"] = "call.execute"; $m->params["id"] = $partycallid; $m->params["callto"] = $destination; $m->Dispatch(); break; } }
/** * Initialize the IVR system if not already initialized, used internally */ private static function InitIVR() { global $yate_ivr_target; global $yate_ivr_register; global $yate_ivr_current; global $yate_ivr_stack; global $yate_ivr_files; if (isset($yate_ivr_register)) { return; } Yate::Debug("IVR::InitIVR()"); $yate_ivr_target = null; $yate_ivr_register = array(); $yate_ivr_current = null; $yate_ivr_stack = array(); $yate_ivr_files = ""; }
function callAnswered($ev, $text, $idParam) { global $param; global $defDelay; $evId = $ev->GetValue($idParam); $evPeerId = $ev->GetValue("peerid"); $outbound = getBoolValue($ev->getValue($param . "_outbound"), false); $tmp = $ev->getValue($param . "_delay"); $delay = $defDelay; if (!empty($tmp)) { $val = (int) $tmp; if ($val >= 0) { $delay = $val; } } Yate::Debug($param . ": Handling " . $ev->name . " text=" . $text . " id=" . $evId . " peerid=" . $evPeerId . " outbound=" . $outbound . " delay=" . $delay); if ($outbound) { if (empty($evPeerId)) { return; } $evId = $evPeerId; } else { if (empty($evId)) { return; } } if ($delay > 0) { setupCall($evId, true, $delay, $text); } else { sendTones($evId, $text); } }
function routeFailure($error, $ev) { $number = $ev->GetValue("called"); Yate::Output("Failed routing dialout to '{$number}' with error '{$error}'"); }
function debug($msg) { Yate::Debug('send_voicem.php: ' . $msg); }
function gotNotify($reason) { global $state; Yate::Debug("gotNotify('{$reason}') state: {$state}"); if ($reason == "replaced") { return; } switch ($state) { case "goodbye": setState(""); break; case "greeting": setState("beep"); break; case "beep": setState("record"); break; default: setState("goodbye"); break; } }
function debug($msg) { Yate::Debug('send_mail.php: ' . $msg); }
/** * This static function initializes globals in the PHP Yate External Module. * It should be called before any other method. * @param $async (optional) True if asynchronous, polled mode is desired * @param $addr Hostname to connect to or UNIX socket path * @param $port TCP port to connect to, zero to use UNIX sockets * @param $role Role of this connection - "global" or "channel" * @return True if initialization succeeded, false if failed */ static function Init($async = false, $addr = "", $port = 0, $role = "") { global $yate_stdin, $yate_stdout, $yate_stderr; global $yate_socket, $yate_debug, $yate_output; $yate_debug = false; $yate_stdin = false; $yate_stdout = false; $yate_stderr = false; $yate_output = false; if ($addr) { $ok = false; if ($port) { $yate_socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); $ok = @socket_connect($yate_socket, $addr, $port); } else { $yate_socket = @socket_create(AF_UNIX, SOCK_STREAM, 0); $ok = @socket_connect($yate_socket, $addr, $port); } if ($yate_socket === false || !$ok) { $yate_socket = false; $yate_stderr = fopen("php://stderr", "w"); Yate::Output("Socket error, initialization failed"); return false; } $yate_output = true; } else { $yate_socket = false; $yate_stdin = fopen("php://stdin", "r"); $yate_stdout = fopen("php://stdout", "w"); $yate_stderr = fopen("php://stderr", "w"); $role = ""; } flush(); set_error_handler("_yate_error_handler"); ob_implicit_flush(1); if ($async && function_exists("stream_set_blocking") && $yate_stdin) { stream_set_blocking($yate_stdin, false); } if ($role) { _yate_print("%%>connect:{$role}\n"); } return true; }
function onFax($id) { Yate::Output("Fax call detected on {$id}"); $m = new Yate("chan.masquerade"); $m->id = ""; // don't notify about message result $m->params["message"] = "call.execute"; $m->params["id"] = $id; // FIXME: generate an unique name for each call $m->params["callto"] = "fax/receive/spool/fax-rx.tif"; $m->params["reason"] = "fax"; $m->Dispatch(); }
function invalidate($address) { global $cache; foreach ($cache as $host => &$entries) { $changed = false; foreach ($entries as $idx => $cached) { if ($cached->address == $address) { Yate::Output("Invalidating {$address} of host {$host}"); unset($entries[$idx]); $changed = true; } } if (count($entries) == 0) { Yate::Debug("Invalidating entire host {$host}"); unset($cache[$host]); } else { if ($changed) { $cache[$host] = array_values($cache[$host]); } } } }
function handle_pear_error($e) { Yate::Output($e->getMessage() . ' ' . print_r($e->getUserInfo(), true)); }
/** * See if this call uses the address book. If so then find the real number the call should be sent to and modify $called param * @param $called Number the call was placed to */ function routeToAddressBook(&$called, $username) { global $adb_keys; // debug("entered routeToAddressBook(called='$called', username='******')"); if (substr($called, 0, strlen($adb_keys)) != $adb_keys) { return; } debug("trying routeToAddressBook(called='{$called}', username='******')"); $number = substr($called, strlen($adb_keys), strlen($called)); $possible_names = get_possible_options($number); $query = "SELECT short_names.number, 1 as option_nr FROM short_names, extensions WHERE extensions.extension='{$username}' AND extensions.extension_id=short_names.extension_id AND short_name IN ({$possible_names}) UNION SELECT number, 2 as option_nr FROM short_names WHERE extension_id IS NULL AND short_name IN ({$possible_names}) ORDER BY option_nr"; $res = query_to_array($query); if (count($res)) { if (count($res) > 1) { Yate::Output("!!!!!!! Problem with finding real number from address book. Multiple mathces. Picking first one"); } $called = $res[0]["number"]; } else { debug("Called number '{$called}' seems to be using the address book. No match found. Left routing to continue."); } return; }
function gotDTMF($text) { global $state; global $mailbox; global $collect_user; global $collect_pass; Yate::Debug("gotDTMF('{$text}') state: {$state}"); switch ($state) { case "user": if ($text == "*") { promptUser(); return; } if ($text == "#") { checkUser(); } else { $collect_user .= $text; } return; case "pass": if ($text == "*") { promptPass(); return; } if ($text == "#") { checkPass(); } else { $collect_pass .= $text; } return; } if ($mailbox == "") { return; } navigate($text); }
function onAnswer($ev, $id, $when) { global $calls; global $codes; if (substr($id, 0, 4) != "sip/") { return; } if (!isset($calls[$id])) { return; } $keys = $codes[rand(0, count($codes) - 1)]; $when += rand(2, 10); $calls[$id] = "{$when}:{$keys}"; Yate::Debug("ANSWER: {$id} '{$keys}'"); }
if ($ev === true) { // Yate::Output("PHP event: empty"); continue; } /* If we reached here we should have a valid object */ switch ($ev->type) { case "incoming": $conn = pg_connect("host=127.0.0.1 dbname=yate user=postgres password="******"SELECT username,password FROM yatet"); Yate::Output("Username : "******" password " . pg_fetch_result($query, 0, 1)); $ev->retval = pg_fetch_result($query, 0, 1); $ev->handled = true; /* This is extremely important. We MUST let messages return, handled or not */ $ev->Acknowledge(); break; case "answer": Yate::Output("PHP Answered: " . $ev->name . " id: " . $ev->id); break; case "installed": Yate::Output("PHP Installed: " . $ev->name); break; case "uninstalled": Yate::Output("PHP Uninstalled: " . $ev->name); break; default: Yate::Output("PHP Event: " . $ev->type); } } Yate::Output("PHP: bye!"); /* vi: set ts=8 sw=4 sts=4 noet: */
function endRoute($callto, $ok, $err) { global $partycallid; global $num; global $collect; if ($ok) { Yate::Output("Overlapped got route: '{$callto}' for '{$collect}'"); $m = new Yate("chan.masquerade"); $m->params["message"] = "call.execute"; $m->params["id"] = $partycallid; $m->params["callto"] = $callto; $m->params["caller"] = $num; $m->params["called"] = $collect; $m->Dispatch(); return; } if ($err != "incomplete") { setState("noroute"); } else { Yate::Output("Overlapped still incomplete: '{$collect}'"); } }