<?
  //extract the correct parameter value. Convert entity id to entity name...
  function prepareMacro($macro,$parameter) {
    global $db, $asteriskAppDelimeter;
    
    $qry="SELECT * FROM cw_macro WHERE id=$macro";
    $resM=$db->Execute($qry);
    $arrM=$resM->FetchRow();
    $macroName=$arrM["name"];

    //extract the parameters
    $params=xml2array($arrM["parameter"]);
    $pArr=explode("|",$parameter);
    if (is_array($pArr))
    foreach ($pArr as $v) {
      $keyvalue=explode("=",$v);
      $selParamsArr[$keyvalue[0]]=$keyvalue[1];
    } 
    
    $arg="";
    if (is_array($params))
    foreach($params as $p) {
      if ($p["name"]!="param") continue;
      $pName=$p["attributes"]["name"];
      $pType=$p["attributes"]["type"];
      switch ($pType) {
        case "QUEUE":
          $r=$db->Execute("SELECT * FROM cw_queue WHERE id='".$selParamsArr[$pName]."'");
          $q=$r->FetchRow();
          $arg.=$asteriskAppDelimeter."\"queue_".$q["id"]."\"";
        break;
        case "GROUP":
        	$grp="";
          $resG=$db->Execute("SELECT * FROM cw_group_entity WHERE group_id='".$selParamsArr[$pName]."'");
          while ($arrG=$resG->FetchRow()) {
            $r=$db->Execute("SELECT name FROM `cw_channel` WHERE `key`='".$arrG["entity_id"]."'");
            $a=$r->FetchRow();
            $grp.=$a["name"]."&";
          }
          if($grp!="") $grp=substr($grp,0,-1);
          $arg.=$asteriskAppDelimeter.$grp;
        break;
        case "EXTENSION":
          $r=$db->Execute("SELECT * FROM `cw_channel` WHERE `key`='".$selParamsArr[$pName]."'");
          $a=$r->FetchRow();
          $arg.=$asteriskAppDelimeter.$a["name"];
        break;
        case "IVR":
          $arg.=$asteriskAppDelimeter."ivr_".$selParamsArr[$pName];
        break;
        case "EMAIL_EXTENSION":
        break;
        case "SOUNDFILE":
        case "TEXTFIELD":
        case "TRUNK":
        case "OUTGOING_RULE":
        case "INCOMING_RULE":
        case "CONFERENCE":
          $arg.=$asteriskAppDelimeter."\"".varEval($selParamsArr[$pName])."\"";
        break;
        default:
          agiError("ERROR: unknown macro parameter type \"".$pType."\"");
        break;
      }
    }
    //return exec string
    return array($macroName,$arg);      
  }
  
  //search for some variable and evaluate it
  function varEval($txt) {
    global $agi;
    if((strpos($txt,"\${")!==false)&&($agi!=null)){
    	$txtArr=explode('${',$txt);
    	if(is_array($txtArr)){
   		  $txt=trim($txtArr[0]);
      	for($i=1;$i<count($txtArr);$i++){
        	$elemArr=explode('}',$txtArr[$i]);
          if((is_array($elemArr))&&(count($elemArr)==2)){
          	$ret=$agi->get_variable($elemArr[0]);
          	$txt.=$ret["data"];
            $txt.=$elemArr[1];
          }else{
          	$txt.="\${".$txtArr[$i];
          }
        }
      }
    }
    /*
    if(substr(trim($txt),0,2)=="\${" and $agi!=null) {
      $ret=$agi->get_variable(substr(trim($txt),2,-1));
      $txt=$ret["data"];
    }
    */
    return $txt;
  }

  function _getGroup($entity_id) {
    global $db;
    $ret=array();
    if ($entity_id=="") return 0;
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=$entity_id";
    $res=$db->Execute($qry);
    while ($arr=$res->FetchRow()) $ret[]=$arr["group_id"];
    return $ret;
  }

  function _getTrunk($r) {
    global $db;
    $ret="";
    $arr=explode("|",$r);
    if (is_array($arr)) 
    foreach ($arr as $t) {
      $qry="SELECT * FROM cw_channel WHERE id=$t";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $ret.=$arr["name"]."&";
    }
    if($ret!="") $ret=substr($ret,0,-1);
    return $ret;
  }
    
  function checkTime($ts=0) {
    global $db;
    if ($ts==0) return true;
    //Get the Time Segment
    $qry="SELECT * FROM cw_time_segment WHERE id=$ts";
    $res=$db->Execute($qry);
    $arrTs=$res->FetchRow();
    $id=$arrTs["id"];
    $name=$arrTs["name"];
    $date_from=$arrTs["date_from"];
    $date_to=$arrTs["date_to"];
    $hour_from=$arrTs["hour_from"];
    $hour_to=$arrTs["hour_to"];
    $week_day=$arrTs["week_day"];
    $day=$arrTs["day"];
    $month=$arrTs["month"];  
    //Check Week Days
    if($week_day!=""){
      $qryTmp="";
      $week_dayArr=explode("|",$week_day);
      foreach ($week_dayArr as $w) {
        $qryTmp.=" OR DAYOFWEEK(NOW())='$w'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    }
    //Check Month Days
    if($day!=""){
      $qryTmp="";
      $dayArr=explode("|",$day);
      foreach ($dayArr as $d) {
        $qryTmp.=" OR DAYOFMONTH(NOW())='$d'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    } 
    //Check Month 
    if($month!=""){
      $qryTmp="";
      $monthArr=explode("|",$month);
      foreach ($monthArr as $m) {
        $qryTmp.=" OR MONTH(NOW())='$m'";
      }    
      $qry.=" AND (".substr($qryTmp,3).")";
    } 
    //Check Date Segment
    if(($date_from!="")OR($date_to!="")OR($hour_from!="")OR($hour_to!="")){
      $qryAllSeg="";
      $date_fromArr=explode("|",$date_from);
      $date_toArr=explode("|",$date_to);
      $hour_fromArr=explode("|",$hour_from);
      $hour_toArr=explode("|",$hour_to); 
      foreach($date_fromArr as $i=>$d) {
        $qrySeg="";
        if ($d != "")                $qrySeg.=" AND DATE(NOW())>=STR_TO_DATE('".$d."','%d-%m-%Y')";
        if ($date_toArr[$i] != "")   $qrySeg.=" AND DATE(NOW())<=STR_TO_DATE('".$date_toArr[$i]."','%d-%m-%Y')";
        if ($hour_fromArr[$i] != "") $qrySeg.=" AND TIME(NOW())>=".extractTime($hour_fromArr[$i]);
        if ($hour_toArr[$i] != "")   $qrySeg.=" AND TIME(NOW())<=".extractTime($hour_toArr[$i]);
        if($qrySeg!="") $qryAllSeg.=" OR (".substr($qrySeg,4).")";
      }
      if($qryAllSeg!="") $qry.=" AND (".substr($qryAllSeg,3).")";
    }
    //Execute query
    $res=$db->Execute($qry);
    //Return    
    if($res===false){
      agiError("  TIMESEGMENT '$name' CHECK: ERROR");
      return false;
    }else if($res->RecordCount()==0){
      agiError("  TIMESEGMENT '$name' CHECK: False");
      return false;
    }else{
      agiError("  TIMESEGMENT '$name' CHECK: True");
      return true;
    }
  }


  
  if (!function_exists("xml2array")) {
  function xml2array ($xml)
  {
     $xmlary = array ();
  
     if ((strlen ($xml) < 256) && is_file ($xml))
       $xml = file_get_contents ($xml);
    
     $ReElements = '/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*?)<(\/\s*\1\s*)>)/s';
     $ReAttributes = '/(\w+)=(?:"|\')([^"\']*)(:?"|\')/';
    
     preg_match_all ($ReElements, $xml, $elements);
     foreach ($elements[1] as $ie => $xx) {
     $xmlary[$ie]["name"] = $elements[1][$ie];
       if ( $attributes = trim($elements[2][$ie])) {
           preg_match_all ($ReAttributes, $attributes, $att);
           foreach ($att[1] as $ia => $xx)
             // all the attributes for current element are added here
             $xmlary[$ie]["attributes"][$att[1][$ia]] = $att[2][$ia];
       } // if $attributes
       
       // get text if it's combined with sub elements
     $cdend = strpos($elements[3][$ie],"<");
     if ($cdend > 0) {
             $xmlary[$ie]["text"] = substr($elements[3][$ie],0,$cdend -1);
         } // if cdend
         
       if (preg_match ($ReElements, $elements[3][$ie])){        
           $xmlary[$ie]["elements"] = xml2array ($elements[3][$ie]);
           }
       else if (isset($elements[3][$ie])){
           $xmlary[$ie]["text"] = $elements[3][$ie];
           }
       $xmlary[$ie]["closetag"] = $elements[4][$ie];
     }//foreach ?
     return $xmlary;
  } 
  }
    
function extractTime($t) {
  $arg="";
  $t=str_replace(",",":",$t);
  $t=str_replace(".",":",$t);
  $arr=explode(":",$t);
  for ($i=0; $i<3; $i++) {
    if (isset($arr[$i])) $arg.=$arr[$i].",";
    else $arg.="0,";
  }
  if(isset($arg)&&($arg!="")){
    $ret="MAKETIME(".substr($arg,0,-1).")";
  }else $ret="";
  return $ret;  
}  
/**
 * Function _metaGroupFromGroup(group_id:int):Group
 * 
 */
function _metaGroupFromGroup($group_id) {
  global $db;
  $qry="SELECT * FROM `cw_group` WHERE `id`=".$group_id;
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    $ret=$arr["type"];
  }
  return $ret;
}

/******************************************************************************/
/***************            OUTGOING FUNCTIONS               ******************/
/******************************************************************************/

/**
 * Function _isPrevDenied(extension:string,ruleset:int,priority:int):boolean
 * return if outgoing condition has been previously denied or not
 */
function _setRuleChannelVariables($variables){
  global $agi;
  if((is_array($variables))&&(count($variables)>0)){
    agiError("  CHANNEL VARIABLES:");
    foreach($variables as $v){
      list($var_name,$var_val)=explode("=",$v,2);
      agiError("    --> $var_name = $var_val");
      $agi->set_variable($var_name, $var_val);
    }
  }else{
    agiError("  CHANNEL VARIABLES: [none]"); 
  }
}
/**
 * Function _isPrevDenied(extension:string,ruleset:int,priority:int):boolean
 * return if outgoing condition has been previously denied or not
 */
function _congestion($message_on_deny){
  global $agi;
  if($message_on_deny){
    agiError("  Play not-available message");
    $agi->stream_file('outbound');
    $agi->stream_file('call');
    $agi->stream_file('T-is-not-available');
  }
  agiError("  ----------------> Congestion");
  $agi->exec("Congestion", '');
}
/**
 * Function _isPrevDenied(extension:string,ruleset:int,priority:int):boolean
 * return if outgoing condition has been previously denied or not
 */ 
function _isPrevDenied($extension,$ruleSetId,$priority){
  global $db;
  $deniedTrunks="";
  $deniedOnRules="";
  $deniedAll=false;
  // retrieve previous denies
  $qry="SELECT r.* , g.type , ge.`group_id`";
  $qry.=" FROM (cw_rule_outgoing AS r LEFT JOIN cw_group AS g ON r.`group` = g.`id`)";
  $qry.=" LEFT JOIN cw_group_entity AS ge ON r.`entity` = ge.`entity_id`";
  $qry.=" WHERE rule=".$ruleSetId."";
  $qry.=" AND r.`priority`<".$priority." AND r.`action`=".RULE_DENY."";
  $qry.=" AND ( r.`group`=".GROUP_OF_ALL." OR g.`type`=".GROUP_OF_PHONENUMBER." OR ge.`group_id`=".GROUP_OF_PHONENUMBER." )";
  $qry.=" ORDER BY r.`priority` DESC";
  $prev_rules=$db->Execute($qry);
  // analyse rules
  while ($rule=$prev_rules->FetchRow()) {
    $id=$rule["id"];
    $entity=$rule["entity"];
    $group=$rule["group"];
    $resurces=$rule["resource"];
    $timesegment=$rule["timesegment"];
    $deniedAll=false;
    // check time segment
    if(checkTime($timesegment)===false) continue;
    // check resurces
    switch ($group){
      // (All) or (All phone number)
      case GROUP_OF_ALL:          
      case GROUP_OF_PHONENUMBER: 
        if($resurces!="0"){
          $deniedTrunks.=_getTrunk($resurces)."&";
          $deniedOnRules.=$id." ";
        }else $deniedAll=true;   
      break;
      // (Class of numbers) or (Group of class of numbers)
      case 0:   
      default: 
        $included=_isIncluded($extension,$entity,$group,false);
        if($included===true){
          if($resurces!="0"){
            $deniedTrunks.=_getTrunk($resurces)."&";
            $deniedOnRules.=$id." ";
          }else $deniedAll=true;
        }
      break;  
    }
    if($deniedAll===true) break; 
  }
  if($deniedAll===true){
    $ret["all"]=true;
    $ret["list"]="";
    agiError("  Previously has been denied all");
  }else{
    if($deniedTrunks!="") $deniedTrunks=substr($deniedTrunks,0,-1);
    $ret["all"]=false;
    $ret["list"]=$deniedTrunks;
    if($deniedOnRules!=""){
      agiError("  Previously has been denied: ");
      agiError("    > on rules:  ".$deniedOnRules);
      agiError("    > on trunks: ".$deniedTrunks);    
    }
    else agiError("  Previously has never been denied");
  }
  return $ret;                  
}
/**
 * Function _getChannelFromEntity(entity_id:int):string
 * extract channel name from exstension entity 
 */ 
function _getChannelFromEntity($entity_id){
  global $db;
  $channelName="";
  //get channel name
  $qry="SELECT name FROM cw_channel WHERE `key`='".$entity_id."'";
  $res=$db->Execute($qry);
  if($res!==false){
    $channelArr=$res->FetchRow();
    $channelName=$channelArr["name"];
  }
  //return
  return $channelName;
}
/**
 * Function _getCid(trunk:int):array(string, string)
 * extract cid_name and cid_number setting for a given trunk 
 */ 
function _getCid($outTrunk){
  global $agi,$db;
  $cid_debug=array();
  $cid["cid_clir"]="0";
  $cid["cid_number"]="";
  $cid["cid_name"]="";
  $cid["status"]="ALLOWED";
  //get channel setting (clir could be set by an application)
  $channelClirArr=$agi->get_variable("CID_CLIR");
  $channelClir=trim($channelClirArr["data"]);
  // -----------------------------------
  // ----------   TRUNK CID   ----------
  // -----------------------------------  
  $qry="SELECT * ";
  $qry.="FROM `cw_channel` ";
  $qry.="WHERE name='".$outTrunk."'";
  $res=$db->Execute($qry);
  $outTrunkArr=$res->FetchRow();
  if(is_array($outTrunkArr)){
    if($outTrunkArr["enable_cid"]==1){
      if($outTrunkArr["force_cid"]==1){
        // -----------------------------------
        // ---------   FORCE CID   -----------
        // -----------------------------------
        if(($outTrunkArr["clir"]==1)&&(($channelClir=="")||($channelClir=="true"))){
          // Trunk is setted to 'force hide'
          agiError("  CLIR: ON (Set by trunk [Force ON])");
          $cid["cid_clir"]="1";
        }else if(($outTrunkArr["clir"]==0)&&(($channelClir=="")||($channelClir=="false"))){
          // Trunk is setted to 'force default'
          agiError("  CLIR: OFF (Set by trunk [Force ON])");
          $cid["cid_clir"]="0";
        }else{
          // Channel set isn't allowed: play congestion
          if($channelClir=="true") $cid["cid_clir"]="1"; else $cid["cid_clir"]="0";
          $cid["status"]="DENIED";
          return $cid;
        }
        $cid["cid_number"]=$outTrunkArr["cid_number"];
        $cid["cid_name"]=$outTrunkArr["cid_name"];
        //if cid_appexten is setted, append user extension to CID Number
        if($outTrunkArr["cid_appexten"]==1){
          $callerChannel=$agi->get_variable("CHANNEL");
          $callerChannelArr=explode("-",$callerChannel["data"]);  
          $callerInfo=$callerChannelArr[0];
          $callerInfoArr=explode("/",$callerInfo);
          $callerTech=$callerInfoArr[0];
          $callerExtension=$callerInfoArr[1];
          $cid["cid_number"].=$callerExtension;
          $cid_debug.="[append extension]";
        }
        agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by trunk [Force ON])");
        return $cid;
      }
      // Trunk is free: look user setting
      else {
        // -----------------------------------
        // ----------   USER CID   -----------
        // -----------------------------------
        $callerChannel=$agi->get_variable("CHANNEL");
        $callerChannelArr=explode("-",$callerChannel["data"]);  
        $callerInfo=$callerChannelArr[0];
        $callerInfoArr=explode("/",$callerInfo);
        $callerTech=$callerInfoArr[0];
        $callerExtension=$callerInfoArr[1];
        switch($callerTech){
          case "SIP":
          case "IAX2":
          default:
            break;
        }
        $qry="SELECT * ";
        $qry.="FROM `cw_cid` AS cid LEFT JOIN `cw_client` AS client ON cid.`extension` = client.`id` ";
        $qry.="WHERE client.`extension`='".$callerExtension."' ";
        $qry.="AND cid.`trunk`='".$outTrunkArr['id']."' ";
        $res=$db->Execute($qry);
        $callerArr=$res->FetchRow();
        if(is_array($callerArr)){
          if($channelClir=="true"){
            agiError("  CLIR: ON (Set by channel [Force OFF])");
            $cid["cid_clir"]="1";
          }else if($channelClir=="false"){
            agiError("  CLIR: OFF (Set by channel [Force OFF])");
            $cid["cid_clir"]="0";
          }else if($callerArr["clir"]==1){
            // User is set to 'clir'
            agiError("  CLIR: ON (Set by user [Force OFF])");
            $cid["cid_clir"]="1";
          } else {
            // User is set 
            agiError("  CLIR: OFF (Set by user [Force OFF])");
            $cid["cid_clir"]="0";
          }
          $cid["cid_number"]=$callerArr["cid_number"];
          $cid["cid_name"]=$callerArr["cid_name"];
          agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by user [Force OFF])");
          return $cid;
        }
        // -----------------------------------
        // --------   NO USER CID   ----------
        // -----------------------------------  
        else{
          if($channelClir=="true"){
            agiError("  CLIR: ON (Set by channel [Force OFF])");
            $cid["cid_clir"]="1";
          }else if($channelClir=="false"){
            agiError("  CLIR: OFF (Set by channel [Force OFF])");
            $cid["cid_clir"]="0";
          }else if($outTrunkArr["clir"]==1){
            // Trunk default is 'hide'
            agiError("  CLIR: ON (Set by trunk [Force OFF])");
            $cid["cid_clir"]="1";
          }else{
            // Trunk default values
            agiError("  CLIR: OFF (Set by trunk [Force OFF])");
            $cid["cid_clir"]="0";
          }
          $cid["cid_number"]=$outTrunkArr["cid_number"];
          $cid["cid_name"]=$outTrunkArr["cid_name"];
          //if cid_appexten is setted, append user extension to CID Number
          if($outTrunkArr["cid_appexten"]==1){
            $callerChannel=$agi->get_variable("CHANNEL");
            $callerChannelArr=explode("-",$callerChannel["data"]);  
            $callerInfo=$callerChannelArr[0];
            $callerInfoArr=explode("/",$callerInfo);
            $callerTech=$callerInfoArr[0];
            $callerExtension=$callerInfoArr[1];
            $cid["cid_number"].=$callerExtension;
            $cid_debug.="[append extension]";
          }
          agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by trunk [Force OFF])");
          return $cid;
        }
      }
    }
    // -----------------------------------
    // ----   PASSTHROUGH USER CID   -----
    // ----------------------------------- 
    else{
      $cidNameArr=$agi->get_variable("CALLERID(name)");
      $cidNumArr=$agi->get_variable("CALLERID(num)");
      $cid["cid_name"]=$cidNameArr["data"];
      $cid["cid_number"]=$cidNumArr["data"];
      if($channelClir=="true"){
        agiError("  CLIR: ON (Set by channel [Force OFF])");
        $cid["cid_clir"]="1";
      }else if($channelClir=="false"){
        agiError("  CLIR: OFF (Set by channel [Force OFF])");
        $cid["cid_clir"]="0";
      }else if($outTrunkArr["clir"]==1){
        $cid["cid_clir"]="1";
        agiError("  CLIR: ON (Set by trunk)");
      }else{
        $cid["cid_clir"]="0";
        agiError("  CLIR: OFF (Set by trunk)");
      }
      agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Passthrough set by trunk)");
      return $cid;
    }
  }
  // Error processing cid
  agiError("  CLIR: OFF (Error processing cid)");
  agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Error processing cid)");
  return $cid;
}
/**
 * Function _chanIsAvail(trunklist:string):string
 * return the first available trunk between trunks on list 
 */
function _chanIsAvail($availableTrunks){
  global $agi;
  //give up mISDN layers
  $trunksArr=explode("&",$availableTrunks);
  if(is_array($trunksArr)){
  	foreach($trunksArr as $trunk){
  		list($tTech,$tPG)=explode("/",$trunk);
      if($tTech=="mISDN"){
  			agiError("  Wake up mISDN layers for Port Group $tPG");
        $agi->exec("misdn_check_l2l1 $tPG|2", '');
      }
    }
  }
  //select an available channel
  $agi->exec("ChanIsAvail $availableTrunks", '');
  $ret=$agi->get_variable("AVAILCHAN");
  if($ret!=""){
    $selectedChannelArr=explode("-",$ret["data"]);
    if(is_array($selectedChannelArr)){ 
      $selectedChannel=$selectedChannelArr[0];
      return $selectedChannel;
    }
    else return "";
  }
  else return "";
}
/**
 * Function _getTrunkFromChannel(channel:string):string
 * extract the trunk from the channel 
 */ 
function _getTrunkFromChannel($outChannel){
  global $db;
  $outChannelArr=explode("-",$outChannel);  
  $outTrunk=$outChannelArr[0];
  $outTrunkArr=explode("/",$outTrunk);
  $outTech=$outTrunkArr[0];
  $outExtension=$outTrunkArr[1];
  switch($outTech){
    case "mISDN":
      $qry="SELECT g.name ";
      $qry.="FROM `cw_isdn_portgroup` AS pg LEFT JOIN `cw_isdn_group` AS g ON pg.`group` = g.`id` ";
      $qry.="WHERE pg.port=".$outExtension."";
      $res=$db->Execute($qry);
      if($res!==false){
        $isdnGroup=$res->FetchRow();
        if(is_array($isdnGroup)) $outExtension="g:".$isdnGroup["name"];
        else return "";
      }else return "";
      break;
    case "Zap":
      $qry="SELECT g.`group` ";
      $qry.="FROM `cw_zapata_group` AS g ";
      $qry.="JOIN  `cw_zapata_portgroup` AS pg ON pg.`group`=g.`id` ";
      $qry.="WHERE pg.port=".$outExtension."";
      $res=$db->Execute($qry);
      if($res!==false){
        $zapGroup=$res->FetchRow();
        if(is_array($zapGroup)) $outExtension="g".$zapGroup["group"];
        else return "";
      }else return "";
      break;
    case "SIP":
    case "IAX2":
    default:
      break;
  }
  $outTrunkName=$outTech."/".$outExtension;
  return $outTrunkName;
}
/**
 * Function _getDiffChannel(permittedList:string,deniedList: string):string
 * return the difference between permitted and denied trunks
 */ 
function _getDiffChannel($permitted,$denied){
  $permittedArr=explode("&",$permitted);  
  $deniedArr=explode("&",$denied);  
  $diff=array_diff($permittedArr,$deniedArr);
  $ret="";
  foreach($diff as $ch){
    $ret.=$ch."&";
  }     
  $ret=substr($ret, 0, -1);
  return $ret;
}
/**
 * Function _isLocalCall(channel: string):boolean
 * return if the caller channel is "Local" or not
 */ 
function _isLocalCall($channel){
  global $db;
  $channelArr=explode("/",$channel);
  if($channelArr[0]=="Local") return true;
  else return false;
}
/**
 * Function _senetizeCalledNumber(number: string, channel: string):string
 * apply to the number the prefix policy for outgoing trunk
 */
function _sanitizeCalledNumber($number,$channel){
  global $db,$agi;
  $special_patterns=array();
  //get channel location settings
  $qry="SELECT l.*,c.`area_code`,c.`prefix_format`,c.`prefix_policy` ";
  $qry.="FROM `cw_channel` AS c ";
  $qry.="JOIN `cw_location` AS l ON l.`id`=c.`location` ";
  $qry.="WHERE c.`name`='".$channel."' ";
  $res=$db->Execute($qry);
  if($res!==false){
    $arr=$res->FetchRow();
    if(is_array($arr)){
      //if trunk's location has area codes but trunk doesn't have one, 
      //set trunk's area code as the first one of the list of its location
      if((trim($arr["area_codes"])!="")&&(trim($arr["area_code"])=="")){
        $area_codes=explode("|",$arr["area_codes"]);
        $area_code=$area_codes[0];
      }else $area_code=$arr["area_code"];
      //extract special patterns list
      if(trim($arr["special_patterns"])!="") $special_patterns=explode("|",$arr["special_patterns"]);
      if(!is_array($special_patterns)) $special_patterns=array();
      //build location array
      $trunkLocation=array(
          "id" => $arr["id"],
          "name" => $arr["name"],
          "int_prefix" => $arr["int_prefix"],
          "country_code" => $arr["country_code"],
          "nat_prefix" => $arr["nat_prefix"],
          "nat_prefix_intl" => $arr["nat_prefix_intl"],
          "special_patterns" => $special_patterns,
          "area_code" => $area_code,
          "prefix_format" => $arr["prefix_format"],
          "prefix_policy" => $arr["prefix_policy"],
        );
    }
  }
  //get client location settings
  $clientLocation=_getCallerLocation();
  //check for errors
  if((!is_array($trunkLocation))||(count($trunkLocation)<1)||
     (!is_array($clientLocation))||(count($clientLocation)<1)){
    agiError("    --> ERROR: unknow location for trunk or client");
    return $number;
  }
  //*****************************************//
  // Trunk and Client have the same Location //
  //*****************************************//
  if($trunkLocation["id"]==$clientLocation["id"]){
    agiError("    --> Client and trunk have the same location [".$trunkLocation["name"]."(".$trunkLocation["int_prefix"].$trunkLocation["country_code"].")]");
    if(trim($clientLocation["area_code"])=="") $c_ac="NONE"; else $c_ac=$clientLocation["area_code"];
    agiError("    --> Client's area code: ".$c_ac);
    if(trim($trunkLocation["area_code"])=="") $t_ac="NONE"; else $t_ac=$trunkLocation["area_code"];
    agiError("    --> Trunk's area code:  ".$t_ac);
    //if number is international (in +XX format or 00XX format)...
    if((substr($number,0,strlen("+"))=="+")|| 
       (substr($number,0,strlen($trunkLocation["int_prefix"]))==$trunkLocation["int_prefix"])){
       //DO NOTHING: number is international
       agiError("    --> DO NOTHING: called number is in international format");
    }
    //if location has no area codes... 
    else if((trim($trunkLocation["area_code"])=="")||(trim($clientLocation["area_code"])=="")){
      //DO NOTHING: no area code, so use the number client call
      agiError("    --> DO NOTHING: called number is national and location has no area code");
    }
    //if number has no area code...
    else if(substr($number,0,strlen($trunkLocation["nat_prefix"]))!=$trunkLocation["nat_prefix"]){
      //check if number match a special patterns
      $match=false;
      if((is_array($trunkLocation["special_patterns"]))&&(count($trunkLocation["special_patterns"])>0)){
        foreach($trunkLocation["special_patterns"] as $pattern){
          $match=_matchPattern($number,$pattern);
          if($match===true) break;
        }
      }
      //if number is a special number...
      if($match===true){
        //DO NOTHING: called number match a special patterns
        agiError("    --> DO NOTHING: called number match a special patterns (".$pattern.")");
      }
      //if client's area code and trunk's area code are the same...
      else if($trunkLocation["area_code"]==$clientLocation["area_code"]){
        //DO NOTHING: no area code, so use the number client call
        agiError("    --> DO NOTHING: called number is local and trunk and client have the same area code");
      }
      //if client's area code and trunk's area code are different...
      else{ 
        //MODIFY NUMBER: prepend to number the national prefix and the client's area code
        $number=$trunkLocation["nat_prefix"].$clientLocation["area_code"].$number;
        agiError("    --> MODIFY NUMBER: called number is local for client but not for trunk");
        agiError("        >ADD trunk area code (new: ".$number.")");
      }
    }
    //if number has area code...
    else{
      //if number's area code doesn't match trunk's one
      if(substr($number,0,strlen($trunkLocation["nat_prefix"].$trunkLocation["area_code"]))!=$trunkLocation["nat_prefix"].$trunkLocation["area_code"]){
        //DO NOTHING: number's area code doesn't match trunk's one
        agiError("    --> DO NOTHING: called number is national and its area code doesn't match trunk's one");
      }
      //if number's area code match the trunk's one
      else{
        //MODIFY NUMBER: number's area code match the trunk's one
        $number=substr($number,strlen($trunkLocation["nat_prefix"].$trunkLocation["area_code"]));
        agiError("    --> MODIFY NUMBER: called number in national and its area code match trunk's one");
        agiError("        >REMOVE trunk area code (new: ".$number.")");
      }
    }
  }  
  //******************************************//
  // Trunk and Client have different Location //
  //******************************************//
  else{
    agiError("    --> Client and trunk have different Location [C:".$clientLocation["name"]."(".$clientLocation["int_prefix"].$clientLocation["country_code"].")|T:".$trunkLocation["name"]."(".$trunkLocation["int_prefix"].$trunkLocation["country_code"].")]");
    if(trim($clientLocation["area_code"])=="") $c_ac="NONE"; else $c_ac=$clientLocation["area_code"];
    agiError("    --> Client's area code: ".$c_ac);
    if(trim($trunkLocation["area_code"])=="") $t_ac="NONE"; else $t_ac=$trunkLocation["area_code"];
    agiError("    --> Trunk's area code:  ".$t_ac);
    //if number is international (in +XX format or 00XX format)...
    if((substr($number,0,strlen("+"))=="+")|| 
       (substr($number,0,strlen($clientLocation["int_prefix"]))==$clientLocation["int_prefix"])){
      //if number is international but it is a national call for trunk
      if((substr($number,0,strlen($clientLocation["int_prefix"].$trunkLocation["country_code"]))==$clientLocation["int_prefix"].$trunkLocation["country_code"])||
         (substr($number,0,strlen("+".$trunkLocation["country_code"]))=="+".$trunkLocation["country_code"])){
        //MODIFY NUMBER: remove international prefix and add new national prefix if needed
        agiError("    --> MODIFY NUMBER: called number is international for client but national for trunk");
        //remove international prefix
        if(substr($number,0,1)=="+"){
          $number=substr($number,strlen("+".$trunkLocation["country_code"]));
        }else{
          $number=substr($number,strlen($clientLocation["int_prefix"].$trunkLocation["country_code"]));
        }
        agiError("       >REMOVE client's international prefix and country code from called number (new: ".$number.")");
        //if not present add trunk national prefix
        if(substr($number,0,strlen($trunkLocation["nat_prefix"]))!=$trunkLocation["nat_prefix"]){
          $number=$trunkLocation["nat_prefix"].$number;
          agiError("       >ADD trunk's national prefix (".$trunkLocation["nat_prefix"].") to called number (new: ".$number.")");
        }else{
          agiError("       >Don't add trunk's national prefix (".$trunkLocation["nat_prefix"].") because number already has it");
        }
        //if trunk has area code and number is a local call for trunk, remove national prefix and area code
        if(trim($trunkLocation["area_code"])!=""){
          if(substr($number,0,strlen($trunkLocation["nat_prefix"].$trunkLocation["area_code"]))==$trunkLocation["nat_prefix"].$trunkLocation["area_code"]){
            $number=substr($number,strlen($trunkLocation["nat_prefix"].$trunkLocation["area_code"]));
            agiError("       >REMOVE trunk's area code (".$trunkLocation["nat_prefix"].$trunkLocation["area_code"].") because number is local for its (new: ".$number.")");
          }else{
            agiError("       >Don't remove trunk's area code (".$trunkLocation["nat_prefix"].$trunkLocation["area_code"].") because number has another one area code");
          }
        }else{
          agiError("       >Don't try to remove trunk's area code because trunk doesn't have it");
        }
      }
      //if number is an international call for both client and trunk
      else{
        //MODIFY NUMBER: replace number's international prefix width trunk's international prefix
        $number=$trunkLocation["int_prefix"].substr($number,strlen($clientLocation["int_prefix"]));
        agiError("    --> MODIFY NUMBER: called number is international for both client and trunk");
        agiError("        >REPLACE number's internationa prefix ".substr($number,0,strlen($clientLocation["int_prefix"]))." width the trunk's one (new: ".$number.")");
      }
    }
    //if number is national or local...
    else{
      //if number is national or client doesn't have area code...
      if((substr($number,0,strlen($clientLocation["nat_prefix"]))==$clientLocation["nat_prefix"])||
              (trim($clientLocation["area_code"])=="")){
        agiError("    --> MODIFY NUMBER: number is national for client");
        //if client location doesn't want national prefix in international format...
        if($clientLocation["nat_prefix_intl"]!=1){
          //MODIFY NUMBER: remove national prefix
          $number=substr($number,strlen($clientLocation["nat_prefix"]));
          agiError("        >REMOVE client's national prefix (".$clientLocation["nat_prefix"].") from called number (new: ".$number.")");
        }else{
          agiError("        >Don't try to remove client's national prefix because location need it");
        }
      }
      //if number is local for client
      else{
        agiError("    --> MODIFY NUMBER: number is local for client");
        //check if number match a special patterns
        $match=false;
        if((is_array($clientLocation["special_patterns"]))&&(count($clientLocation["special_patterns"])>0)){
          foreach($clientLocation["special_patterns"] as $pattern){
            $match=_matchPattern($number,$pattern);
            if($match===true) break;
          }
        }
        //if number is a special number...
        if($match===true){
          //DO NOTHING: called number match a special patterns
          agiError("        >Don't add client's area code because number match a special patterns (".$pattern.")");
        }
        //if number is not special...
        else{
          //MODIFY NUMBER: add client's area code
          $number=$clientLocation["area_code"].$number;
          agiError("        >ADD client's area code (".$clientLocation["area_code"].") to called number (new: ".$number.")");
          //if client location need national prefix in international format...
          if($clientLocation["nat_prefix_intl"]==1){
            //MODIFY NUMBER: add national prefix
            $number=$clientLocation["nat_prefix"].$number;
            agiError("        >ADD client's national prefix (".$clientLocation["nat_prefix"].") to called number (new: ".$number.")");
          }else{
            agiError("        >Don't add client's national prefix because location doesn't need it");
          }
        }
      }
      //trunk's international prefix and client's country code to the number
      $number=$trunkLocation["int_prefix"].$clientLocation["country_code"].$number;
      agiError("        >ADD trunk's international prefix (".$trunkLocation["int_prefix"].") and client's country code (".$clientLocation["country_code"].") to called number (new: ".$number.")");
    }
  }
  //*****************************//
  // Apply trunk prefix settings //
  //*****************************//
  // International call in +XX format
  if((substr($number,0,strlen("+"))=="+")&&
     (substr($number,strlen("+"),strlen($trunkLocation["country_code"]))!=$trunkLocation["country_code"])){
    agiError("  PREFIX: call to ".$number." (international)");
    if($trunkLocation["prefix_format"]==2){
      //force 00XX format (always replace +XX with 00XX)
      $number=$trunkLocation["int_prefix"].substr($number,strlen("+"));
      agiError("    --> FORCE 00XX format (".$number.")");
    }else{
      agiError("    --> DO NOTHING: number format is allowed");  
    }
  }
  // International call in 00XX format
  else if((substr($number,0,strlen($trunkLocation["int_prefix"]))==$trunkLocation["int_prefix"])&&
          (substr($number,strlen($trunkLocation["int_prefix"]),strlen($trunkLocation["country_code"]))!=$trunkLocation["country_code"]) ){
    agiError("  PREFIX: call to ".$number." (international)");
    if($trunkLocation["prefix_format"]==1){
      //force +XX format (always replace 00XX with +XX)
      $number="+".substr($number,strlen($trunkLocation["int_prefix"]));
      agiError("    --> FORCE +XX format (".$number.")");
    }else{
      agiError("    --> DO NOTHING: number format is allowed");  
    }
  }
  // National call with international prefix in +XX format
  else if(substr($number,0,(strlen("+")+strlen($trunkLocation["country_code"])))=="+".$trunkLocation["country_code"]){
    agiError("  PREFIX: call to ".$number." (national with international prefix)");
    if($trunkLocation["prefix_policy"]==1){
      //remove prefix from the beginning of the number
      $number=substr($number,(strlen("+")+strlen($trunkLocation["country_code"])));
      agiError("    --> REMOVE international prefix (".$number.")");
      //if number need it, add national prefix
      if(substr($number,0,strlen($trunkLocation["nat_prefix"]))!=$trunkLocation["nat_prefix"]){
        //Add national prefix to the number
        $number=$trunkLocation["nat_prefix"].$number;
        agiError("    -> ADD national prefix (".$number.")"); 
      }
    }else if($trunkLocation["prefix_format"]==2){
      //force 00XX format (always replace +XX with 00XX)
      $number=$trunkLocation["int_prefix"].substr($number,strlen("+"));
      agiError("    --> FORCE 00XX format (".$number.")");
    }else{
      agiError("    --> DO NOTHING: number format is allowed");
    }
  }
  // National call with international prefix in 00XX format
  else if(substr($number,0,(strlen($trunkLocation["int_prefix"])+strlen($trunkLocation["country_code"])))==$trunkLocation["int_prefix"].$trunkLocation["country_code"]){
    agiError("  PREFIX: call to ".$number." (national with international prefix)");
    if($trunkLocation["prefix_policy"]==1){
      //remove prefix from the beginning of the number
      $number=substr($number,(strlen($trunkLocation["int_prefix"])+strlen($trunkLocation["country_code"])));
      agiError("    --> REMOVE international prefix (".$number.")");
      //if number need it, add national prefix
      if(substr($number,0,strlen($trunkLocation["nat_prefix"]))!=$trunkLocation["nat_prefix"]){
        //Add national prefix to the number
        $number=$trunkLocation["nat_prefix"].$number;
        agiError("    --> ADD national prefix (".$number.")"); 
      }
    }else if($trunkLocation["prefix_format"]==1){
       //force +XX format (always replace 00XX with +XX)
      $number="+".substr($number,strlen($trunkLocation["int_prefix"]));
      agiError("    --> FORCE +XX format (".$number.")");
    }else{
      agiError("    --> DO NOTHING: number format is allowed");
    }
  }
  // Call with no international prefix
  else{
    agiError("  PREFIX: call to ".$number." (national)");
    //if trunk policy is ADD national prefix in international format
    if($trunkLocation["prefix_policy"]==2){
      //if trunk need it and number hasn't got it...
      if(($trunkLocation["nat_prefix_intl"]==1)&&
         (substr($number,0,strlen($trunkLocation["nat_prefix"]))!=$trunkLocation["nat_prefix"])){
        //Add national prefix to the number
        $number=$trunkLocation["nat_prefix"].$number;
        agiError("    --> ADD national prefix (".$number.")");
      }
      //else if trunk doesn't want it and number has got it...
      else if(($trunkLocation["nat_prefix_intl"]==0)&&
         (substr($number,0,strlen($trunkLocation["nat_prefix"]))==$trunkLocation["nat_prefix"])){
        //Remove national prefix from the number
        $number=substr($number,strlen($trunkLocation["nat_prefix"]));
        agiError("    --> REMOVE national prefix (".$number.")");
      }
      if($trunkLocation["prefix_format"]==1){
        //add prefix in +XX format
        $number="+".$trunkLocation["country_code"].$number;
        agiError("    --> ADD prefix in +XX format (".$number.")");
      }else{
        //add prefix in 00XX format
        $number=$trunkLocation["int_prefix"].$trunkLocation["country_code"].$number;
        agiError("    --> ADD prefix in 00XX format (".$number.")");
      }
    }else{
      agiError("    --> DO NOTHING: number format is allowed");
    }
  }
  //return number
  return $number;
}
/**
 * Function _getCallerLocation():LocationStructure
 * extract caller from channel variable and retrieve its location settings
 */
function _getCallerLocation(){
  global $db,$agi;
  $clientLocation=array();
  $special_patterns=array();
  //get caller prefix settings
  $callerChannel=$agi->get_variable("CHANNEL");
  $callerChannelArr=explode("-",$callerChannel["data"]);  
  $callerChannelName=$callerChannelArr[0];
  $qry="SELECT l.*,c.`area_code`,c.`prefix_format`,c.`prefix_policy` ";
  $qry.="FROM `cw_channel` AS c ";
  $qry.="JOIN `cw_location` AS l ON l.`id`=c.`location` ";
  $qry.="WHERE c.`name`='".$callerChannelName."' ";
  $res=$db->Execute($qry);
  if($res!==false){
    $arr=$res->FetchRow();
    if(is_array($arr)){
      //if client's location has area codes but client doesn't have one, 
      //set client's area code as the first one of the list of its location
      if((trim($arr["area_codes"])!="")&&(trim($arr["area_code"])=="")){
        $area_codes=explode("|",$arr["area_codes"]);
        $area_code=$area_codes[0];
      }else $area_code=$arr["area_code"];
      //extract special patterns list
      if(trim($arr["special_patterns"])!="") $special_patterns=explode("|",$arr["special_patterns"]);
      if(!is_array($special_patterns)) $special_patterns=array();
      //build location array
      $clientLocation=array(
          "id" => $arr["id"],
          "name" => $arr["name"],
          "int_prefix" => $arr["int_prefix"],
          "country_code" => $arr["country_code"],
          "nat_prefix" => $arr["nat_prefix"],
          "nat_prefix_intl" => $arr["nat_prefix_intl"],
          "special_patterns" => $special_patterns,
          "area_code" => $area_code,
          "prefix_format" => $arr["prefix_format"],
          "prefix_policy" => $arr["prefix_policy"],
        );
    }
  }
  //return
  return $clientLocation;
}
/******************************************************************************/
/***************            INCOMING FUNCTIONS               ******************/
/******************************************************************************/
/**
 * Function _getCallInfo(channel:String):array(int, string)
 * extract from incoming call the channel type and the called number 
 */ 
function _getCallInfo($channel){
  global $db,$agi;
  $ret=array("channelTech" => "", "calledNumber" => "");
  //Extract channel type
  $retrievedCalledChannelArr=explode("/",$channel);
  if(is_array($retrievedCalledChannelArr)){
    $channelType = $retrievedCalledChannelArr[0];
    //Extract called number
    if($channelType=="SIP"){ //SIP CALL
      $sipToHeader=$agi->get_variable("SIP_HEADER(TO)");
      $sipFromHeader=$agi->get_variable("SIP_HEADER(FROM)");
      if($sipToHeader["data"]!=""){
        // TO HEADER
        if((strpos($sipToHeader["data"], "<")!==false)&&(strpos($sipToHeader["data"], ">")!==false)  ){
          //sintax type 1: <sip:xxxxxxxxxx@xxx.xxx.xxx.xxx>
          $sipTo=substr($sipToHeader["data"], 0, strpos($sipToHeader["data"], ">")).">";
          $calledNumber=substr($sipTo, (strpos($sipTo,":") + 1), ((strpos($sipTo,"@")) - (strpos($sipTo,":")) - 1));
        }else{
          //sintax type 2: sip:xxxxxxxxxx@xxx.xxx.xxx.xxx
          $sipTo=$sipToHeader["data"];
          $calledNumber=substr($sipTo, (strpos($sipTo,":") + 1), ((strpos($sipTo,"@")) - (strpos($sipTo,":")) - 1));
        }
        // FROM HEADER
        if((strpos($sipFromHeader["data"], "<")!==false)&&(strpos($sipFromHeader["data"], ">")!==false)){
          //sintax type 1: <sip:xxxxxxxxxx@xxx.xxx.xxx.xxx> 
          $sipFrom=substr($sipFromHeader["data"], 0, strpos($sipFromHeader["data"], ">")).">";  
        }else{
          //sintax type 2: sip:xxxxxxxxxx@xxx.xxx.xxx.xxx
          $sipFrom=$sipFromHeader["data"];
        } 
        agiError("[SIP CALL]");
        agiError("   FROM: ".$sipFrom);
        agiError("   TO: ".$sipTo);
        agiError("   CALLED: ".$calledNumber);
      }
      $ret["channelTech"]=SIP;
      $ret["calledNumber"]=$calledNumber;
    } 
  }
  return $ret;
}
/**
 * Function _getAccountInfo(channelId:int,$calledNumber:String):array(int, string, int, string, string)
 * extract provider account's informations
 */ 
function _getCalledAccount($channelId,$calledNumber){
  global $db;
  $calledMatch=false;   
  //verify type
  $qry="SELECT ch.* , en.`type` ";
  $qry.="FROM `cw_channel` AS ch JOIN `cw_entity` AS en ON ch.`key` = en.`id` ";
  $qry.="WHERE ch.`id`=$channelId";
  $res=$db->Execute($qry);
  if($res!==false){ 
    $ch=$res->FetchRow();
    if($ch["type"]==PROVIDER_ACCOUNT){
      //get all relative provider's accounts
      $qry="SELECT * ";
      $qry.="FROM `cw_provider_account` AS pa JOIN `cw_channel` AS ch ON pa.`id` = ch.`key`";
      $qry.="WHERE pa.`provider`=(SELECT `provider` FROM `cw_provider_account` WHERE `id`=".$ch["key"].")";
      $res=$db->Execute($qry);
      //find called account
      while(($accArr=$res->FetchRow())&&($calledMatch===false)){
        $accNumbers=explode("|",$accArr["msns"]);
        if(is_array($accNumbers)){ 
          foreach($accNumbers as $accNumber){
            $calledMatch=_matchPattern($calledNumber,$accNumber);
            if($calledMatch===true){
              $ret["id"]=$accArr["key"];
              $ret["name"]=$accArr["name"];
              $ret["channel"]=$accArr["id"];
              $ret["msns"]=$accArr["msns"];
              $ret["rule"]=$accArr["rule"];
              break;
            }
          }
        }
      }
    }
  }
  return $ret;
}
/**
 * Function _isCallerIncluded(channelId:int,$calledNumber:String):boolean
 * return if the caller match with the caller specified in an incoming rule
 */ 
function _isCallerIncluded($callerid,$callertype,$triggeredCallerid){
  global $db,$agi;
  $match=false;
  switch ($callertype) {
    case CALLER_ALL:
      agiError("    --> match for all pattern");
      $match=true;
    break;
    case CALLER_ANONYMOUS:
      if($triggeredCallerid=="ANONYMOUS") {
        agiError("    --> search for pattern ANONYMOUS : ! MATCH !");
        $match=true;
      } else { 
        $match=false;
        agiError("    --> search for pattern ANONYMOUS : NO MATCH");
      }
    break;
    case CALLER_ID:
      if($triggeredCallerid==$callerid){
        agiError("    --> search for pattern $callerid : ! MATCH !");
        $match=true;
      } else {
        agiError("    --> search for pattern $callerid : NO MATCH");
        $match=false;
      }
    break;
    case CALLER_CLASS:
      $match=_isIncluded($triggeredCallerid,$callerid,0,true);
    break;
    case CALLER_GROUP:
      $match=_isIncluded($triggeredCallerid,0,$callerid,true);
    break;
    default:
      agiError("ERROR: unknown callertype ($callertype) in incoming.inc");
      $match=false;
    break;
  }
  return $match;
}
/**
 * Function _getCallerCidName(callernumber:String):String
 * return the caller name which match the caller number in contact list
 * and is set to authoritative
 */ 
function _getCallerCidName($callernumber){
  global $db;
  $callernumber=trim($callernumber);
  if($callernumber!=""){
  	$qry="SELECT cid_name ";
		$qry.="FROM `cw_contacts` ";
    $qry.="WHERE `read`=1 ";
    $qry.="AND ( ".
    	"((`phonenumber` LIKE '".$callernumber."') AND (`phonenumber_on_cid`=1)) ".
    	"OR ((`mobile` LIKE '".$callernumber."') AND (`mobile_on_cid`=1)) ".
      ") ";
    $qry.="LIMIT 1 ";
    $res=$db->Execute($qry);
    if(($res!==false)&&($c=$res->FetchRow())){
      return trim($c["cid_name"]);
    }
  }
  return "";
}
/**
 * Function _cleanCidNumber(number:String,channel_id:int):String
 * return the cid number cleaned of interfational number if it's a national call
 */
function _cleanCidNumber($number,$channel_id){
  global $db;
  $trunkLocation=array();
  //get channel location settings
  $qry="SELECT l.* ";
  $qry.="FROM `cw_channel` AS c ";
  $qry.="JOIN `cw_location` AS l ON l.`id`=c.`location` ";
  $qry.="WHERE c.`id`='".$channel_id."' ";
  $res=$db->Execute($qry);
  if($res!==false){
  	$arr=$res->FetchRow();
  	if(is_array($arr)){
  	  $trunkLocation=array(
  	      "int_prefix" => $arr["int_prefix"],
  	      "country_code" => $arr["country_code"]
  	    );
  	}
  }
  //clean number
  if((is_array($trunkLocation))&&(count($trunkLocation)>0)){
    if(substr($number,0,strlen($trunkLocation["int_prefix"].$trunkLocation["country_code"]))===($trunkLocation["int_prefix"].$trunkLocation["country_code"])){
      $number=substr($number,strlen($trunkLocation["int_prefix"].$trunkLocation["country_code"]));
    }
    else if(substr($number,0,strlen("+".$trunkLocation["country_code"]))===("+".$trunkLocation["country_code"])){
      $number=substr($number,strlen("+".$trunkLocation["country_code"]));
    }
  }
  //return
  return $number;
}
/**
 * Function _verifyJump(ruleset:String):array(id:int,name:string)
 * return the target of the jump of the rulest. If no jump is set, return an
 * empty array
 */ 
function _verifyJump($ruleset){
  global $db;
  $ret=array();
  //get jump
	$qry="SELECT rj.*,r.`name` ";
  $qry.="FROM `cw_rule_jump` AS rj ";
  $qry.="LEFT JOIN `cw_rule` AS r ON r.`id`=rj.`ruleset_to` ";
  $qry.="WHERE rj.`ruleset_from`='".$ruleset."' ";
  $res=$db->Execute($qry);
  if($res!==false){
    if($arr=$res->FetchRow()){
      //verify jump validity
      $qry="SELECT * FROM `cw_rule_jump` WHERE id='".$arr["id"]."' AND NOW()<'".$arr["end_of_life"]."'";
      $res=$db->Execute($qry);
      if($res!==false){
        $q=$res->RecordCount();
        if($q>0){
          return array(
              "id"=>$arr["ruleset_to"],
              "name"=>$arr["name"],
              "until"=>$arr["end_of_life"]
            );
        }else{
          $qry="DELETE FROM `cw_rule_jump` WHERE id='".$arr["id"]."'";
          $db->Execute($qry);
        }
      }
    }
  }
  return "";
}
/******************************************************************************/
/******************            IVR FUNCTIONS               ********************/
/******************************************************************************/
/**
 * Function _getIvrNode(id:int):Array
 * 
 */
function _getIvrNode($id){
  global $db;
  $ret=array();
  //get node structure
  $qry="SELECT * FROM cw_ivr WHERE id=".$id;
  $res=$db->Execute($qry);
  if($res!==false){
    $ret=$res->FetchRow();
    //add children to node structure
    $ret["children"]=array();
    $qry="SELECT * FROM cw_ivr WHERE parent=".$id." ORDER BY `order`";
    $res=$db->Execute($qry);
    if($res!==false){
      while($arr=$res->FetchRow()){
        $ret["children"][]=$arr;
      }
    }
  }  
  return $ret;
}
/**
 * Function _getAudioFilePath(id:int):String
 * 
 */
function _getAudioFilePath($id){
  global $db;
  $qry="SELECT * FROM cw_sounds_file WHERE id=".$id;
  $res=$db->Execute($qry);
  if($res!==false){
    $arr=$res->FetchRow();
    $ret=$arr["filepath"];
  }
  return $ret;
}
      
/******************************************************************************/
/****************            SHARED FUNCTIONS               *******************/
/******************************************************************************/
/**
 * Function _isIncluded(extension:string, entity:int, group:int):boolean
 * 
 */
function _isIncluded($extension,$entity,$group,$output=true){
  global $db;
  $included=false;
  //clean number from international prefix if country code is caller one
  $clientLocation=_getCallerLocation();
  if((is_array($clientLocation))&&(count($clientLocation)>0)){
    if(substr($extension,0,(strlen("+")+strlen($clientLocation["country_code"])))=="+".$clientLocation["country_code"]){
      $extension=substr($extension,(strlen("+")+strlen($clientLocation["country_code"])));
      if($output===true) agiError("    --> clean extension: national call in international format ($extension)");
    }else if(substr($extension,0,(strlen($clientLocation["int_prefix"])+strlen($clientLocation["country_code"])))==$clientLocation["int_prefix"].$clientLocation["country_code"]){
      $extension=substr($extension,(strlen($clientLocation["int_prefix"])+strlen($clientLocation["country_code"])));
      if($output===true) agiError("    --> clean extension: national call in international format ($extension)");
    }
  }
  if($entity > 0){ // class of number
    $qry="SELECT * FROM cw_number WHERE id=".$entity;
    $numbers=$db->Execute($qry);
    while($row=$numbers->FetchRow()){
      $patternArr=explode("|",$row["number"]);
      foreach($patternArr as $pattern){
        $included=_matchPattern($extension,$pattern);
        if($included===true) {
          if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
          break;       
        }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
      }
    }
  }else if($group > 0) { // group of class of numbers
    if(($group!=1)&&($group!=GROUP_OF_PHONENUMBER)){ 
      $qry="SELECT * FROM `cw_group_entity` WHERE `group_id`=".$group;
      $classes=$db->Execute($qry);
      while($row=$classes->FetchRow()){
        $qry="SELECT * FROM cw_number WHERE id=".$row["entity_id"];
        $numbers=$db->Execute($qry);
        while($row=$numbers->FetchRow()){
          $patternArr=explode("|",$row["number"]);
          foreach($patternArr as $pattern){
            $included=_matchPattern($extension,$pattern);
            if($included===true) {
              if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
              break;       
            }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
          }
        }
        if($included===true) break;  
      }
    }else{ //$group==1 --> destination * || group==5 --> destination ALL_PHONENUMBERS
      $included=true; 
      if($output===true) agiError("    --> match for all pattern");
    }
  }
  return $included;
}
/**
 * Function _matchPattern(extension:string, pattern:string):boolean
 * 
 */ 
function _matchPattern($ext,$pattern){
  $debug=false;
  // pattern is a match case (return false if different)
  if($pattern[0]!='_'){
    if($pattern==$ext) return true;
    else return false;
  }
  // pattern is a regular expression (return false at the first difference)
  if($debug===true)agiError("(Pattern) $pattern <==> $ext (Number)");
  $pattern=substr($pattern,1);  // remove "_"  
  $pat_pos=0;
  $ext_pos=0;
  while($pat_pos<strlen($pattern)){
    switch($pattern[$pat_pos]){
        case "+":    
        case "0":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
        case "6":
        case "7":
        case "8":
        case "9":
          if($pattern[$pat_pos]!=$ext[$ext_pos]) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "X":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(0,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "Z":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(1,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "N":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(2,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break; 
        case "[":
          if($debug===true) agiError("    |> \"[\""); 
          $remaining=substr($pattern,$pat_pos);
          $end_pos=strpos($remaining,"]");
          if($end_pos===false){
            agiError("PARSING ERROR: check pattern definition ($pattern)");
            return false;
          }
          $p_content=substr($remaining,1,($end_pos)); 
          $p_found=false;
          $p_end=false;
          $j=0;
          while(($j<(strlen($p_content)-1))&&($p_found===false)){
            $next=$j+1;
            switch($p_content[$next]){
              case "0":
              case "1":
              case "2":
              case "3":
              case "4":
              case "5":
              case "6":
              case "7":
              case "8":
              case "9":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "]":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "-":
                $range_start=$j;
                $range_end=($j+2);
                if(in_array($ext[$ext_pos],range($p_content[$range_start],$p_content[$range_end]))) $p_found=true;
                if($p_content[($j+3)]=="]") $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." in [".$p_content[$range_start]."-".$p_content[$range_end]."]");
                $j=($j+3); 
                break;
              default:
                agiError("PARSING ERROR: check pattern definition ($pattern)");
                return false;
                break;
            }
            if($p_end===true) break;
          }
          if($p_found===false) return false;
          $pat_pos=($pat_pos+(strlen($p_content))+1);
          $ext_pos++;
          if($debug===true) agiError("    |> \"]\""); 
          break;  
        case ".":
          if($ext[$ext_pos]=="") return false;
          if($debug===true) agiError("    |> \".\""); 
          return true;
          break;
        default:
          agiError("PARSING ERROR: check pattern definition ($pattern)");
          return false;
          break;
     }
  }
  if($debug===true) agiError("EXIT: if  ".strlen($ext)." == ".$ext_pos."  --> MATCH"); 
  if(strlen($ext)==$ext_pos) return true;
  return false;
}
/**
 * Function agiError(string:String):int
 * print string to php's error log 
 */  
function agiError($string){
  global $agiDebug,$agi;
  if($agiDebug===true){ 
    $id=$agi->get_variable("UNIQUEID");
    $string="[".$id["data"]."] ".$string;
    error_log($string);
    return 1;
  }
  else return 0; 
}
if(!function_exists("errArr")){
  function errArr($arr){
    if (count($arr)==0) error_log("ok!");
    else {
      ob_start();
        print_r($arr);
      $contents=ob_get_contents();
      ob_end_clean();
      error_log($contents);
    }
    return;
  }
}
?>