function getSpeedDial($card, &$dialnum) { global $a2b; global $agi; // SPEED DIAL HANDLER if (($sp_prefix = getAGIconfig('speeddial_prefix', NULL)) != NULL) { if (strncmp($dialnum, $sp_prefix, strlen($sp_prefix)) == 0) { // translate the speed dial. $QRY = str_dbparams($a2b->DBHandle(), "SELECT phone, name FROM speeddials WHERE card_id = %#1 AND speeddial = %2", array($card['id'], substr($dialnum, strlen($sp_prefix)))); $agi->conlog($QRY, 3); $res = $a2b->DBHandle()->Execute($QRY); // If the rate engine has anything to Notice/Warn, display that.. if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Speed Dial: query error!', 2); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 2); if (getAGIconfig('say_errors', true)) { $agi->stream_file('allison2', '#'); } break; } elseif ($res->EOF) { $agi->verbose('Speed Dial: no result.', 2); } $arr_speeddial = $res->fetchRow(); $agi->conlog('Speed Dial : found ' . $arr_speeddial['phone'], 4); $dialnum = $arr_speeddial['phone']; } } }
$agi->conlog('Card: ' . print_r($card, true), 4); //TODO: fix lang if ($card['status'] != 1) { switch ($card['status']) { case 8: //disabled card in booth $agi->stream_file('prepaid-no-card-entered', '#'); break; case 5: $agi->stream_file('prepaid-card-expired', '#'); break; default: $agi->verbose('Card status: ' . $card['status'] . ', exiting.', 2); } break; } // Here, we're authorized.. if (getVoucher($card) !== null) { // perhaps stream the credit? break; } // or continue, if voucher didn't succeed. } if ($card && !empty($card['locked'])) { ReleaseCard($card); } $agi->conlog('Goodbye!', 3); if (getAGIconfig('say_goodbye', true) && $agi->is_alive) { $agi->stream_file('prepaid-final', '#'); } $agi->hangup();
function formatDialstring_peer($dialn, &$route, &$card, $do_param = true) { global $a2b; global $agi; $dbhandle = $a2b->DBHandle(); if ($route['stripdigits'] > 0) { $dialnum = substr($route['dialstring'], $route['stripdigits']); } else { $dialnum = $route['dialstring']; } $bind_str = '%dialtech/%dialname'; switch ($route['trunkfmt']) { case 4: $qry = str_dbparams($dbhandle, 'SELECT dialtech, dialname FROM cc_dialpeer_local_v ' . 'WHERE useralias = %1', array($dialnum)); $bind_str = '%dialtech/%dialname'; if (strlen($route['providertech'])) { $qry .= str_dbparams($dbhandle, ' AND dialtech = %1', array($route['providertech'])); } // If the trunk specifies an "ip", aliases among the corresponding numplan will be queried // else, the numplan *must* be the same with that of the card. // It would be wrong not to specify a numplan, since aliases accross them are not unique! if (strlen($route['providerip'])) { $qry .= str_dbparams($dbhandle, ' AND numplan_name = %1', array($route['providerip'])); } else { $qry .= str_dbparams($dbhandle, ' AND numplan = %#1', array($card['numplan'])); } break; case 6: // hardcode search into same numplan! $qry = str_dbparams($dbhandle, 'SELECT * FROM cc_dialpeer_remote_v ' . 'WHERE useralias = %1 AND numplan = %#2', array($dialnum, $card['numplan'])); $bind_str = $route['providertech'] . '/' . $route['providerip']; break; case 7: case 15: $dnum = explode('-', $dialnum); if ($dnum[0] == 'L') { $dnum[0] = $card['numplan']; } $qry = str_dbparams($dbhandle, 'SELECT dialtech, dialname FROM cc_dialpeer_local_v ' . 'WHERE useralias = %2 AND numplan = %#1 ', $dnum); if (strlen($route['providertech'])) { $qry .= str_dbparams($dbhandle, ' AND dialtech = %1', array($route['providertech'])); } $bind_str = '%dialtech/%dialname'; $agi->conlog("Query: {$qry}", 3); break; case 8: $dnum = explode('-', $dialnum); if ($dnum[0] == 'L') { $dnum[0] = $card['numplan']; } $qry = str_dbparams($dbhandle, 'SELECT * FROM cc_dialpeer_remote_v ' . 'WHERE useralias = %2 AND numplan = %#1', $dnum); $agi->conlog("Query: {$qry}", 3); $bind_str = $route['providertech'] . '/' . $route['providerip']; break; } $qry .= ';'; //$agi->conlog("Find peer from ". $qry,4); if (!$bind_str) { return false; } $res = $dbhandle->Execute($qry); if (!$res) { $agi->verbose('Cannot dial peer: ' . $dbhandle->ErrorMsg()); if (getAGIconfig('say_errors', true)) { $agi->stream_file('allison2', '#'); } return false; } if ($res->EOF) { $agi->verbose("Peer dial: cannot find peer " . $dialnum, 2); //$agi-> stream_file("prepaid-dest-unreachable",'#'); return null; } // Feature! If more than one registrations exist, call all of them in // parallel! $peer_rows = array(); while ($row = $res->fetchRow()) { $peer_rows[] = str_alparams($bind_str, $row); } $str = ''; if ($do_param) { if ($agi->astmajor == "1.6") { $str .= getAGIconfig('dialcommand_param', ',60,iL(%timeout)%param'); } else { $str .= getAGIconfig('dialcommand_param', '|60|iL(%timeout)%param'); } $str = str_alparams($str, array('dialnum' => $dialnum, 'dialnumber' => $dialn, 'dialstring' => $route['dialstring'], 'destination' => $route['destination'], 'trunkprefix' => $route['trunkprefix'], 'tech' => $route['providertech'], 'providerip' => $route['providerip'], 'prefix' => $route['prefix'], 'param' => $route['trunkparm'], 'cardnum' => $card['username'], 'stimeout' => $route['tmout'], 'timeout' => 1000 * $route['tmout'])); } return implode('&', $peer_rows) . $str; }
function getCard_acode() { global $a2b; global $agi; $dbhandle = $a2b->DBHandle(); if (!isset($agi->request['agi_accountcode']) || strlen($agi->request['agi_accountcode']) < 3) { $agi->verbose("No accountcode for auth", 2); return false; } $acodes = explode(':', $agi->request['agi_accountcode']); switch ($acodes[0]) { case 'card': $res = $dbhandle->Execute('SELECT card.id, tariffgroup AS tgid, card.username, card.status, ' . 'card.numplan, card.useralias, card.features ' . 'FROM cc_card_dv AS card ' . 'WHERE card.id = ? LIMIT 1 ;', array($acodes[1])); break; case 'booth': $res = $dbhandle->Execute('SELECT card.id, tariffgroup AS tgid, card.username, card.status, ' . 'card.numplan, card.useralias, card.features ' . 'FROM cc_card_dv AS card, cc_booth ' . 'WHERE cc_booth.cur_card_id = card.id ' . 'AND cc_booth.id = ? LIMIT 1 ;', array($acodes[1])); break; case 'remote-agent': //used by remote hosts $agid = $acodes[1]; $anivar = $agi->get_variable('CALLERID(ANI)'); if ($anivar['result'] == 0) { $agi->verbose('No ANI set for remote-agent auth'); return false; } // TODO: after this, we MUST reset ANI to the proper value, or else // we may leak it to the provider! $acodes = explode(':', $anivar['data']); switch ($acodes[0]) { case 'card': $res = $dbhandle->Execute('SELECT card.id, tariffgroup AS tgid, card.username, card.status, ' . 'card.numplan, card.useralias, card.features ' . 'FROM cc_card_dv AS card ' . 'WHERE card.id = ? AND agentid = ? LIMIT 1 ;', array($acodes[1], $agid)); break; case 'booth': $res = $dbhandle->Execute('SELECT card.id, tariffgroup AS tgid, card.username, card.status, ' . 'card.numplan, card.useralias, card.features ' . 'FROM cc_card_dv AS card, cc_booth ' . 'WHERE cc_booth.cur_card_id = card.id ' . 'AND cc_booth.id = ? AND cc_booth.agentid = ? LIMIT 1 ;', array($acodes[1], $agid)); break; default: $agi->verbose('Unknown accountcode at remote: ' . $anivar['data']); return false; } break; default: $agi->verbose('Unknown accountcode: ' . $agi->request['agi_accountcode']); return false; } if (!$res) { $agi->verbose('Cannot auth-acode: ' . $dbhandle->ErrorMsg()); if (getAGIconfig('say_errors', true)) { $agi->stream_file('allison2', '#'); } return false; } if ($res->EOF) { $agi->verbose("Accountcode: no card for " . $acodes[0] . ": " . $acodes[1] . " ", 2); if ($acodes[0] == 'booth') { $agi->stream_file("prepaid-no-card-entered", '#'); } else { $agi->stream_file("prepaid-auth-fail", '#'); } return false; } $agi->conlog('Auth-acode: found card.', 4); return $res->fetchRow(); }
/** * Function refill_card_with_voucher **/ function getVoucher($card) { global $a2b; global $agi; $dbhandle = $a2b->DBHandle(); $agi->conlog('Voucher refill with card', 4); $vtimeout = getAGIconfig('voucher-timeoute', 8000); $vmaxlen = getAGIconfig('voucher-maxlen', 15); $vminlen = getAGIconfig('voucher-minlen', 5); $vprompt = getAGIconfig('voucher-prompt', 'prepaid-voucher_enter_number'); $vprompt_nexist = getAGIconfig('voucher-prompt-nexist', 'prepaid-voucher_does_not_exist'); $vprompt_refill = getAGIconfig('voucher-prompt-refill', 'prepaid-account_refill'); $vprompt_no_entered = getAGIconfig('voucher-prompt-no-entered', 'prepaid-no-voucher-entered'); $vprompt_invalid = getAGIconfig('voucher-prompt-invalid', 'prepaid-invalid-voucher'); $agi->conlog('Voucher-ivr: asking for Voucher', 4); $res_dtmf = $agi->get_data($vprompt, $vtimeout, $vmaxlen); $agi->conlog('Voucher-ivr: result ' . print_r($res_dtmf, true), 3); if (!isset($res_dtmf['result'])) { $agi->conlog('No Voucher entered', 2); $agi->stream_file($vprompt_no_entered, '#'); return null; } $vouchernum = $res_dtmf['result']; if (strlen($vouchernum) < $vminlen || strlen($vouchernum) > $vmaxlen) { $agi->conlog('Invalid Voucher', 2); $agi->stream_file($vprompt_invalid, '#'); return null; } // CALL STORED PROCEDURE FOR VOUCHER $QRY = str_dbparams($a2b->DBHandle(), 'SELECT * FROM card_use_voucher (%1, %2);', array($card['id'], $vouchernum)); $agi->conlog($QRY, 3); $res = $a2b->DBHandle()->Execute($QRY); // If the rate engine has anything to Notice/Warn, display that.. if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $emsg = $dbhandle->ErrorMsg(); if (substr($emsg, 0, 23) == 'ERROR: card_use_voucher') { $msga = explode('|', $emsg); $agi->verbose('Could not use voucher: ' . $msga[3]); //$agi->conlog("Message: " . print_r($msga,true),4); switch ($msga[1]) { case 'voucher-no-find': //$agi->stream_file('prepaid-card-in-use','#'); break; case 'voucher-zero': case 'conv_currency-failed': case 'conv_currency-failed-zero': //TODO break; default: $agi->conlog('Unknown result from card_use_voucher: ' . $msga[1], 3); } } else { $agi->verbose('Could not use voucher : ' . $emsg); } $agi->stream_file($vprompt_invalid, '#'); return null; } if ($res->EOF) { $agi->verbose('No used voucher in card_use_voucher(), why?'); return null; } $agi->conlog('Unknown result from card_use_voucher: ' . $msga[1], 3); $row = $res->fetchRow(); if (empty($row['card_use_voucher'])) { $agi->verbose('Fail to fetch on voucher ! '); return false; } $agi->conlog('Voucher used. Amount of credit added : ' . $row['card_use_voucher'], 3); $agi->stream_file($vprompt_refill, '#'); // TODO : play the Amount of credit added return true; }
/** Special dial modes, like VoiceMail etc */ function dialSpecial($dialnum, $route, $card, $card_money, &$last_prob, $agi, $attempt) { global $a2b; global $mode; global $did_clidname; global $new_clid; $dbhandle = $a2b->DBHandle(); if ($route['stripdigits'] > 0) { $dialnum = substr($route['dialstring'], $route['stripdigits']); } else { $dialnum = $route['dialstring']; } $dialn = null; switch ($route['trunkfmt']) { case 9: // Group dial require_once "groupdial.inc.php"; return groupDial($dialnum, $route, $card, $card_money, $last_prob, $agi, $attempt); break; // group // group case 10: $dialn = array($card['numplan'], $dialnum); case 11: if (!$dialn) { // case 11 $dialn = explode('-', $dialnum); if ($dialn[0] == 'L') { $dialn[0] = $card['numplan']; } } //todo: locale field! $qry = str_dbparams($dbhandle, "SELECT email, 'C' AS locale FROM cc_card, cc_card_group\n\t\t\tWHERE cc_card.grp = cc_card_group.id AND cc_card_group.numplan = %#1\n\t\t\t AND cc_card.useralias = %2 AND cc_card.status =1;", $dialn); $res = $dbhandle->Execute($qry); if (!$res) { $agi->verbose('Cannot query peer: ' . $dbhandle->ErrorMsg()); return false; } else { if ($res->EOF) { $agi->conlog("Query: {$qry}", 5); $agi->verbose("Peer email: cannot find peer " . $dialnum, 2); return false; } } $row = $res->fetchRow(); if (empty($row['email'])) { $agi->conlog("User at {$dialnum}, has no email, skipping.", 3); return true; } if (empty($route['providerip'])) { $tmpl = 'missed-call'; } else { $tmpl = $route['providerip']; } // TODO: put more data in params.. // TODO: do NOT issue callerid, when callingpres prohibits is. It is bad ! if (!empty($did_clidname)) { $clname = $did_clidname; } else { $clname = $agi->request['agi_calleridname']; } if (!empty($new_clid)) { $clid = $new_clid; } else { $clid = $agi->request['agi_callerid']; } $params = array('clid' => $clid, 'agiclid' => $agi->request['agi_callerid'], 'clname' => $clname, 'agiclname' => $agi->request['agi_calleridname'], 'last' => $last_prob); $res = $dbhandle->Execute("SELECT create_mail(?,?,?,?);", array($tmpl, $row['email'], $row['locale'], arr2url($params))); if (!$res) { $agi->verbose('Cannot create mail: ' . $dbhandle->ErrorMsg(), 2); return false; } $str = $dbhandle->NoticeMsg(); if ($str) { $agi->verbose($str, 3); } // FIXME: how well should the email be quoted before fed to the AGI? $agi->conlog("Mail notification queued for " . str_replace("\n", '', $row['email'])); return true; case 12: // local voicemail $dialn = array($card['numplan'], $dialnum); case 13: // cross-nplan voicemail // We obviously cannot get any voicemail if the caller has // hung up. if ($last_prob == 'cancel') { return false; } if (!$dialn) { // case 11 $dialn = explode('-', $dialnum); if ($dialn[0] == 'L') { $dialn[0] = $card['numplan']; } } //todo: locale field! $qry = str_dbparams($dbhandle, "SELECT username, email, 'C' AS locale FROM cc_card, cc_card_group\n\t\t\tWHERE cc_card.grp = cc_card_group.id AND cc_card_group.numplan = %#1\n\t\t\t AND cc_card.useralias = %2 AND cc_card.status =1;", $dialn); $res = $dbhandle->Execute($qry); if (!$res) { $agi->verbose('Cannot query peer: ' . $dbhandle->ErrorMsg()); return false; } else { if ($res->EOF) { $agi->conlog("Query: {$qry}", 5); $agi->verbose("Peer voicemail: cannot find peer " . $dialnum, 2); return false; } } $row = $res->fetchRow(); $vmcontext = getAGIconfig('vmcontext', 'default'); $mailbox = $row['username']; if (!empty($vmcontext)) { $mailbox .= '@' . $vmcontext; } $agi->conlog("Voicemail for {$mailbox}", 3); if ($last_prob == 'busy') { $mopts = 'b'; } else { $mopts = 'u'; } $mopts .= $route['trunkparm']; // usually, the 's' $uniqueid = $agi->request['agi_uniqueid']; // . $attempt; $res = $a2b->DBHandle()->Execute('INSERT INTO cc_call (cardid, attempt, cmode, ' . 'sessionid, uniqueid, nasipaddress, src, ' . 'calledstation, destination, ' . 'srid, brid, tgid, trunk) ' . 'VALUES( ?,?,?,?,?,?,?,?,?,?,?,?,?) RETURNING id;', array($card['id'], $attempt, $mode, $agi->request['agi_channel'], $uniqueid, NULL, $card['username'], $dialnum, $route['destination'], $route['srid'], $route['brid'], $route['tgid'], $route['trunkid'])); if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call start in db!'); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 2); // This error may mean that trunk is in use etc. // If call cannot be billed, we'd better abort it. $last_prob = 'call-insert'; return false; } elseif ($res->EOF) { $agi->verbose('Cannot mark call start in db: EOF!'); $last_prob = 'call-insert'; return false; } $call_id = $res->fetchRow(); $agires = $agi->exec('VoiceMail', array($mailbox, $mopts)); // $agi->conlog("VM result: ". print_r($agires,true),5); $vmstatus = $agi->get_variable('VMSTATUS'); $agi->conlog("VM status: " . print_r($vmstatus, true), 5); $res = $dbhandle->Execute('UPDATE cc_call SET ' . 'stoptime = now(), sessiontime = EXTRACT(epoch from (now() - starttime))::INTEGER, tcause = ?, hupcause = 0 ' . 'WHERE id = ? ;', array($vmstatus['data'], $call_id['id'])); if ($notice = $dbhandle->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call end in db! (will NOT bill)', 0); $agi->conlog($dbhandle->ErrorMsg(), 2); } else { if ($dbhandle->Affected_Rows() < 1) { $agi->verbose('Could not mark call end! (will NOT bill)', 1); } } if ($vmstatus['data'] != 'SUCCESS') { return false; } // TODO: here, try to create an email with the voicemail... /* if (empty($route['providerip'])) $tmpl='missed-call'; else $tmpl=$route['providerip']; // TODO: put more data in params.. // TODO: do NOT issue callerid, when callingpres prohibits is. It is bad ! $params = array( clid => $agi->request['agi_callerid'], clname=> $agi->request['agi_calleridname'], last => $last_prob); $res = $dbhandle->Execute( "SELECT create_mail(?,?,?,?);", array($tmpl,$row['email'],$row['locale'], arr2url($params))); if (!$res) $agi->verbose('Cannot create mail: '. $dbhandle->ErrorMsg(),2); return false; } $str = $dbhandle->NoticeMsg(); if ($str) $agi->verbose($str,3); // FIXME: how well should the email be quoted before fed to the AGI? $agi->conlog("Mail notification queued for ". str_replace("\n",'',$row['email'])); */ return true; case 14: // voicemail Main (user's menu) $vmcontext = getAGIconfig('vmcontext', 'default'); $mailbox = $card['username']; if (!empty($vmcontext)) { $mailbox .= '@' . $vmcontext; } $agi->conlog("Voicemail Main for {$mailbox}", 3); $uniqueid = $agi->request['agi_uniqueid']; $res = $a2b->DBHandle()->Execute('INSERT INTO cc_call (cardid, attempt, cmode, ' . 'sessionid, uniqueid, nasipaddress, src, ' . 'calledstation, destination, ' . 'srid, brid, tgid, trunk) ' . 'VALUES( ?,?,\'vm-main\',?,?,?,?,?,?,?,?,?,?) RETURNING id;', array($card['id'], $attempt, $agi->request['agi_channel'], $uniqueid, NULL, $card['username'], $dialnum, $route['destination'], $route['srid'], $route['brid'], $route['tgid'], $route['trunkid'])); if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call start in db!'); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 2); // This error may mean that trunk is in use etc. // If call cannot be billed, we'd better abort it. $last_prob = 'call-insert'; return false; } elseif ($res->EOF) { $agi->verbose('Cannot mark call start in db: EOF!'); $last_prob = 'call-insert'; return false; } $call_id = $res->fetchRow(); $agires = $agi->exec('VoiceMailMain', array($mailbox, $route['trunkparm'])); // $hangupcause=$agi->get_variable('HANGUPCAUSE'); // // $answeredtime = $agi->get_variable("ANSWEREDTIME"); // if ($answeredtime['result']== 0) // $answeredtime['data'] =0; // $dialstatus = $agi->get_variable("DIALSTATUS"); // // $dialedtime = $agi->get_variable("DIALEDTIME"); // if ($dialedtime['result']== 0) // $dialedtime['data'] =0; // // $agi->conlog("Dial result: ".$dialstatus['data'].'('. $hangupcause['data']. ') after '. $answeredtime['data'].'sec.',2); $res = $dbhandle->Execute('UPDATE cc_call SET ' . 'stoptime = now(), sessiontime = EXTRACT(epoch from (now() - starttime))::INTEGER, tcause = \'ANSWER\', hupcause = 0 ' . 'WHERE id = ? ;', array($call_id['id'])); if ($notice = $dbhandle->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call end in db! (will NOT bill)', 0); $agi->conlog($dbhandle->ErrorMsg(), 2); } else { if ($dbhandle->Affected_Rows() < 1) { $agi->verbose('Could not mark call end! (will NOT bill)', 1); } } //if ($vmstatus['data']!='SUCCESS') // return false; return true; default: $agi->verbose("Cannot dial special with format " . $route['trunkfmt'], 3); return false; } }
} } //for } catch (Exception $ex) { // Here we handle signals received $agi->verbose("Exception at dial:" . $ex->getMessage()); @syslog("Exception at dial:" . $ex->getMessage()); ReleaseCard($card); $card = null; break; } } // while //TODO: set hangup cause accordingly if ($last_prob) { $agi->conlog("Last problem: " . $last_prob, 2); } if ($card && !empty($card['locked'])) { ReleaseCard($card); } if ($last_call) { if (empty($last_call['cause_ext'])) { $last_call['cause_ext'] = $last_prob; } releaseCall1($a2b->DBHandle(), $last_call); } $agi->conlog('Goodbye!', 3); if (getAGIconfig('say_did_goodbye', false) && $agi->is_alive) { $agi->stream_file('prepaid-final', '#'); } $agi->hangup();
function groupDial($dialnum, $route, $card, $card_money, &$last_prob, $agi, $attempt) { global $a2b, $mode; // First, parse *our* string, so that we can form the // destinations. $gstr = $route['providerip']; //TODO: need to combine providerIP & dialstring? if (empty($gstr)) { $gstr = $dialnum; } $agi->conlog("Group dialnum: \"{$gstr}\"", 5); $gdests = groupstr_analyze($gstr); // Then, find all rateengine results for each destination $QRY = str_dbparams($a2b->DBHandle(), 'SELECT * FROM RateEngine2(%#1, ?, %#2, now(), %3);', array($card['tgid'], $card['numplan'], $card_money['base'])); $all_routes = array(); foreach ($gdests as $gdest) { $agi->conlog($QRY . " [?= {$gdest}]", 4); $res = $a2b->DBHandle()->Execute($QRY, array($gdest)); // If the rate engine has anything to Notice/Warn, display that.. if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Rate engine: query error!', 2); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 3); break; } elseif ($res->EOF) { $agi->verbose('Rate engine: no result.', 3); continue; } $routes = $res->GetArray(); // now, find the first route that might work foreach ($routes as $aroute) { if ($aroute['tmout'] < getAGIconfig('min_duration_2call', 30)) { $agi->conlog('Call will be too short: ', $aroute['tmout'], 4); $last_prob = 'min-length'; continue; } if ($aroute['tmout'] > getAGIconfig('max_call_duration', 604800)) { $aroute['tmout'] = getAGIconfig('max_call_duration', 604800); $agi->conlog('Call truncated to: ', $aroute['tmout'], 4); } // Check if trunk needs a feature subscription if (!empty($aroute['trunkfeat'])) { // This field comes as a string, convert to array.. if (!empty($card['features']) && !is_array($card['features'])) { $card['features'] = sql_decodeArray($card['features']); } if (empty($card['features']) || !in_array($aroute['trunkfeat'], $card['features'])) { if (empty($last_prob)) { $last_prob = 'no-feature'; } $agi->conlog("Call is missing feature \"" . $aroute['trunkfeat'] . "\", skipping route.", 3); $agi->conlog("Features: " . print_r($card['features'], true), 4); continue; } // feature found! $agi->conlog('Call using feature: ' . $aroute['trunkfeat'], 5); } $dialstr = formatDialstring($dialnum, $aroute, $card, false); if ($dialstr === null) { $last_prob = 'unreachable'; continue; } elseif (!$dialstr) { $last_prob = 'no-dialstring'; continue; } elseif ($dialstr === true) { // We cannot use other special trunks in group. continue; } if ($aroute['clidreplace'] !== NULL && $mode != 'did') { $new_clid = str_alparams($aroute['clidreplace'], array('useralias' => $card['useralias'], 'nplan' => $card['numplan'], 'callernum' => $agi->request['agi_callerid'])); } else { $new_clid = $agi->request['agi_callerid']; } // add this route to the available ones $all_routes[] = array('r' => $aroute, 'str' => $dialstr, 'clid' => $new_clid); //but then, discard all other possibilities for this destination. break; } } //$agi->conlog("Group dialing routes: ".print_r($all_routes,true),4); if (empty($all_routes)) { $last_prob = 'call-fail'; return false; } // Now, iterate over all the routes to find some useful stuff $agg_route = array(); $agg_strs = array(); foreach ($all_routes as $aroute) { // Try to have a common CLID. if (!isset($agg_route['clid'])) { $agg_route['clid'] = $aroute['clid']; } elseif ($agg_route['clid'] != $aroute['clid']) { $agg_route['clid'] = ''; } if (!isset($agg_route['tmout'])) { $agg_route['tmout'] = $aroute['r']['tmout']; } elseif ($agg_route['tmout'] > $aroute['r']['tmout']) { $agg_route['tmout'] = $aroute['r']['tmout']; } $agg_strs[] = $aroute['str']; } $agg_route['str'] = implode('&', $agg_strs); //$agi->conlog("Group dialing routes: ".print_r($agg_route,true),4); $dialstr = $agg_route['str'] . str_alparams(getAGIconfig('dialcommand_group_param', '|60|iL(%timeout)%param'), array('dialstring' => $agg_route['str'], 'trunkprefix' => $route['trunkprefix'], 'tech' => $route['providertech'], 'providerip' => $route['providerip'], 'prefix' => $route['prefix'], 'param' => $route['trunkparm'], 'cardnum' => $card['username'], 'stimeout' => $route['tmout'], 'timeout' => 1000 * $route['tmout'])); // Place the call! $agi->conlog("Setting clid to : " . $agg_route['clid'], 3); $agi->set_variable('CALLERID(num)', $agg_route['clid']); if (!empty($route['call_uniqueid'])) { $uniqueid = $route['call_uniqueid']; } else { // Construct a unique id with .. + trunkid. $uniqueid = $agi->request['agi_uniqueid'] . '-' . $route['trunkid']; } $res = $a2b->DBHandle()->Execute('INSERT INTO cc_call (cardid, attempt, cmode, ' . 'sessionid, uniqueid, nasipaddress, src, ' . 'calledstation, destination, ' . 'srid, brid, tgid, trunk) ' . 'VALUES( ?,?,?,?,?,?,?,?,?,?,?,?,?) RETURNING id;', array($card['id'], $attempt, $mode, $agi->request['agi_channel'], $uniqueid, NULL, $card['username'], $dialnum, $route['destination'], $route['srid'], $route['brid'], $route['tgid'], $route['trunkid'])); if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call start in db!'); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 2); // This error may mean that trunk is in use etc. // If call cannot be billed, we'd better abort it. $last_prob = 'call-insert'; return false; } elseif ($res->EOF) { $agi->verbose('Cannot mark call start in db: EOF!'); $last_prob = 'call-insert'; return null; } $call_id = $res->fetchRow(); $agi->conlog('Start call ' . $call_id['id'], 4); $agi->conlog("Dial group@" . $route['trunkcode'] . " : {$dialstr}", 3); $attempt++; $call_res = $agi->exec('Dial', $dialstr); //TODO: if record, stop $hangupcause = $agi->get_variable('HANGUPCAUSE'); $answeredtime = $agi->get_variable("ANSWEREDTIME"); if (empty($answeredtime['data']) || $answeredtime['result'] == 0) { $answeredtime['data'] = 0; } $dialstatus = $agi->get_variable("DIALSTATUS"); $dialedtime = $agi->get_variable("DIALEDTIME"); if ($dialedtime['result'] == 0) { $dialedtime['data'] = 0; } // We are special, we need to learn who answered here! $dialedpeer = $agi->get_variable("DIALEDPEERNUMBER"); $agi->conlog("Group result: " . $dialstatus['data'] . '@' . $dialedpeer['data'] . '(' . $hangupcause['data'] . ') after ' . $answeredtime['data'] . 'sec.', 2); //$agi->conlog("After dial, answertime: ".print_r($answeredtime,true)); //TODO: SIP, ISDN extended status $can_continue = false; $cause_ext = ''; switch ($dialstatus['data']) { case 'BUSY': $last_prob = 'busy'; break; case 'ANSWERED': case 'ANSWER': case 'CANCEL': // Now, try to see who answered if (!empty($dialedpeer['data'])) { $agi->conlog("Searching who answered..", 4); foreach ($all_routes as $aroute) { $pos = strpos($aroute['str'], '/'); if ($pos === false) { $str2 = $aroute['str']; } else { $str2 = substr($aroute['str'], $pos + 1); } if ($dialedpeer['data'] == $str2) { $agi->conlog("Found: " . $aroute['str'], 4); $route = $aroute['r']; break; } } } $last_prob = ''; break; case 'CONGESTION': case 'CHANUNAVAIL': $last_prob = 'call-fail'; $can_continue = true; break; case 'NOANSWER': $last_prob = 'no-answer'; $can_continue = true; break; default: $agi->verbose("Unknown status: " . $dialstatus['data'], 2); } $res = $a2b->DBHandle()->Execute('UPDATE cc_call SET ' . 'stoptime = now(), sessiontime = ?, tcause = ?, hupcause = ?, ' . 'cause_ext =?, startdelay =?, ' . 'destination =?, srid =?, brid = ?, trunk =? ' . 'WHERE id = ? ;', array($answeredtime['data'], $dialstatus['data'], $hangupcause['data'], $cause_ext, $dialedtime['data'] - $answeredtime['data'], $route['destination'], $route['srid'], $route['brid'], $route['trunkid'], $call_id['id'])); if ($notice = $a2b->DBHandle()->NoticeMsg()) { $agi->verbose('DB:' . $notice, 2); } if (!$res) { $agi->verbose('Cannot mark call end in db! (will NOT bill)', 0); $agi->conlog($a2b->DBHandle()->ErrorMsg(), 2); } return !$can_continue; }