<?
/**
 * VoiceOne Webservices Methods
 *  
 * @package voiceone_webservices
 * @subpackage ast_commands
 */
//******************************************************************************
//******************           PUBLIC FUNCTIONS            *********************
//******************************************************************************
// ---- Function ast_originate -------------------------------------------------
/**
* Originate a call through asterisk proxy manager. This method open a socket to the proxy
* and send in it login command, originate command and logoff command. 
* Socket settings are stored in config.inc.php
*
* @param username:String -> Asterisk extension to alert before starting call
* @param password:String -> Password linked to the username
* @param extension:String -> Phonenumber to dial
* @param timeout:int -> Time in which user's phones ring, waiting for response
*
* @return:String -> error message or result of the originate command
*/
function ast_originate($username,$password,$extension,$timeout,$waitforresponse=true){
	global $db,$floapi,$API_user,$API_pass,$API_host,$API_PROXY_enabled,
         $API_PROXY_host,$API_PROXY_port,$API_user,$API_pass;
  if((!is_numeric($timeout))||($timeout<0)) $timeout=10000;
  //get data
  $callerid="";
  if(trim($extension)=="") return "ERROR: missing extension to call";
  if((trim($username)!="")||(trim($password)!="")){ 
 		$qry="SELECT c.`technology`, r.`name` as rulename ";
    $qry.="FROM `cw_client` AS c ";
    $qry.="JOIN `cw_rule` AS r ON r.`id`=c.`rule` ";
    $qry.="WHERE c.`extension`='".trim($username)."' ";
    $qry.="AND c.`password`='".trim($password)."' ";
    $qry.="LIMIT 1; ";
    $res=$db->Execute($qry);
    $q=$res->RecordCount();
    if(($q>0)&&($arr=$res->FetchRow())){
      $rule=trim($arr["rulename"]);
      switch($arr["technology"]){
        case SIP:
        		$trunk="SIP/".trim($username);
            $qry="SELECT callerid FROM `sip_buddies` WHERE `name`='".trim($username)."' LIMIT 1; ";
            $res=$db->Execute($qry);
            if(($res!==false)&&($arr=$res->FetchRow())){
            	$callerid=$arr["callerid"];
            }
        	break; 
				case IAX2: 
						$trunk="IAX2/".trim($username);
            $qry="SELECT callerid FROM `iax_buddies` WHERE `name`='".trim($username)."' LIMIT 1; ";
            $res=$db->Execute($qry);
            if(($res!==false)&&($arr=$res->FetchRow())){
            	$callerid=$arr["callerid"];
            }
        	break;
  			case MISDN: 
				case ZAP:
        default:
        	return "ERROR: unsupported user's technologies";
      } 
    }else{
      $qry="SELECT v.`name`, v.`surname`, v.`extension`, r.`name` as rulename ";
      $qry.="FROM `cw_user` AS u ";
      $qry.="JOIN `cw_virtual` AS v ON u.`entity_id`=v.`id` ";
      $qry.="JOIN `cw_rule` AS r ON r.`id`=v.`rule` ";
      $qry.="WHERE u.`username`='".trim($username)."' ";
      $qry.="AND u.`password`='".trim($password)."' ";
      $qry.="LIMIT 1; ";
      $res=$db->Execute($qry);
      $q=$res->RecordCount();
      if(($q>0)&&($arr=$res->FetchRow())){
        $rule=trim($arr["rulename"]);
        $trunk="Local/".trim($username)."@".$rule;
        $qry="SELECT callerid FROM `sip_buddies` WHERE `name`='".trim($username)."' LIMIT 1; ";
        $callerid=trim($arr["name"])." ".trim($arr["surname"])." <".trim($arr["extension"]).">";
      }else return "ERROR: login fail";
    }
  }else return "ERROR: invalid username and password";
  //do request
  if($API_PROXY_enabled==0){
  	$floapi = @new floAPI($API_user, $API_pass, $API_host);
    if($floapi->socket=="") return "ERROR: Unable to connect via API ".$errnum." ".$errstr;
    else{
      $params=array(
          "Channel"   => $trunk,
          "Context"   => $rule,
          "Exten"     => trim($extension),
          "Callerid"  => $callerid,
          "Priority"  => 1,
          "Timeout"   => $timeout
        );
      //exec request
      $ret=$floapi->request("Originate", $params);
      //sleep(($timeout/1000)); // waiting for originate is compleated
      $floapi->close();
      if($ret!==false) return "OK: command executed through api";
      else return "ERROR: wrong server response";
    }
  }else{
    //prepare requests
    $actionid="orig".trim($username)."_".rand(100,999);
    $req_login="Action: Login\r\nUsername: ".trim($API_user)."\r\nSecret: ".trim($API_pass)."\r\n\r\n";
    $req_filter="ProxyAction: SetAutoFilter\r\nAutoFilter: on\r\n\r\n";
    $req_orig="Action: Originate\r\nChannel: ".$trunk."\r\nContext: ".$rule."\r\nExten: ".trim($extension)."\r\nCallerid: ".$callerid."\r\nPriority: 1\r\nTimeout: ".$timeout."\r\nActionID: ".$actionid."\r\n\r\n";
    // ASYNCRONOUS: fputs($socket, "Async: yes\r\n\r\n" );
    $req_logoff="Action: Logoff\r\n\r\n";
    //exec requests
    $sk=@fsockopen($API_PROXY_host,$API_PROXY_port,$errnum,$errstr,60);
    if(!is_resource($sk)) return "ERROR: Unable to connect ".$errnum." ".$errstr;
  	fputs($sk,$req_login); 		// login
    fputs($sk,$req_filter);		// set filter for response
    fputs($sk,$req_orig);			// originate call
    if($waitforresponse===false){
      fputs($sk,$req_logoff);		// logoff
      fclose($sk);
      return "OK: command executed through proxy";
    }else{
      sleep(($timeout/1000)+2); // waiting for originate response before logoff
      fputs($sk,$req_logoff);		// logoff
      //read response
      $response=array();
    	while(!feof($sk)){
    		$row=fgets($sk,2048);
        $response[]=substr($row,0,-1);
    	}
      fclose($sk);
      //parse response
      $orig_response=ast_ParserXML($response,$actionid);
     	//return
      if(trim($orig_response)!="") return $orig_response;
      else return "ERROR: wrong server response";
    }
  }
}
// ---- Function sendCommand ($cmd:string):int ---------------------------------
/**
* Send a command with the Asterisk API Manager interface
*
* @param cmd:String -> command to execute on asterisk
*
* @return:String -> command's return
*/ 
function sendCommand($cmd) {
  global $floapi,$API_user,$API_pass,$API_host,$API_PROXY_enabled,$API_PROXY_host,$API_PROXY_port;
  //Check if asterisk is ON
  $status=pbxSwitch("status");
  if($status!="ON"){
    error_log("WARNING: pbx off (try to execute command \"$cmd\")");
    return 0;
  }
  //Connect to asterisk via API
  if(($API_PROXY_enabled==0)||(true)){ // !!! FORCE DIRECTLY CONNECT TO ASTERISK !!!
    $floapi = @new floAPI($API_user, $API_pass, $API_host);
    if($floapi->socket=="") return "ERROR: Unable to connect via API ".$errnum." ".$errstr;
    else{
      //exec request
      $ret=$floapi->request("COMMAND", array("COMMAND" => $cmd));
      $floapi->close();
      if(trim($ret)!="") return $ret;
      else return "ERROR: Wrong server response";
    }
  }
  //Connect to asterisk via PROXY
  else{
  	//prepare requests
    $actionid="cmd".trim($API_user)."_".rand(100,999);
    $req_login="Action: Login\r\nUsername: ".trim($API_user)."\r\nSecret: ".trim($API_pass)."\r\n\r\n";
    $req_filter="ProxyAction: SetAutoFilter\r\nAutoFilter: on\r\n\r\n";
    $req_cmd="Action: Command\r\nCommand: $cmd\r\nActionID: ".$actionid."\r\n\r\n";
    $req_logoff="Action: Logoff\r\n\r\n";
    //exec requests
    $sk=@fsockopen($API_PROXY_host,$API_PROXY_port,$errnum,$errstr,60);
    if(!is_resource($sk)) return "ERROR: Unable to connect via Proxy ".$errnum." ".$errstr;
  	fputs($sk,$req_login); 		// login
    fputs($sk,$req_filter);		// set filter for response
    fputs($sk,$req_cmd);			// exec command
    sleep(1); 								// waiting for command response before logoff
    fputs($sk,$req_logoff);		// logoff
    //read response
    $response=array();
  	while(!feof($sk)){
  		$row=fgets($sk,2048);
      $response[]=substr($row,0,-1);
  	}
    fclose($sk);
    //parse response
    $cmd_response=ast_ParserXML($response,$actionid);
   	//return
    if(trim($cmd_response)!="") return $cmd_response;
    else return "ERROR: Wrong server response";
  }
}

//******************************************************************************
//*****************           PRIVATE FUNCTIONS            *********************
//******************************************************************************
// ---- Function ast_ParserXML -------------------------------------------------
/**
* PRIVATE: parses xml response data and extracts in an array the output of command 
* linked to passed action id 
*
* @param data:Array -> data to parse lines array
* @param actionid:String -> command reference
*
* @return:Array -> array of xml fileds of command's output
*/
function ast_ParserXML($data,$actionid){
	$match=false;
	$block=array();
	if(is_array($data)){
  	foreach($data as $line){
    	if((trim($line)=="")||(substr(trim($line),0,1)!="<")||(substr(trim($line),0,2)=="</")) continue;
			if( ((strlen($line)>=23)&&(substr($line,0,23)=="<AsteriskManagerOutput>")) 			||
      		((strlen($line)>=28)&&(substr($line,0,28)=="<AsteriskManagerProxyOutput>")) ){
      	if($match===true) break;
        else $block=array();            
      }else{
      	$line=substr($line,strpos($line,"<")+1,(strpos($line,"/>")-strpos($line,"<")-1));
        $lineArr=explode(" Value=",$line);  //DEPRECATED IN PHP 5.3
       	$lineArr=explode(" Value=",$line);
        if((is_array($lineArr))&&(count($lineArr)==2)){
        	//check if block is the response of the request
          if(($lineArr[0]=="ActionID")&&(str_replace("\"","",$lineArr[1])==$actionid)) $match=true;
          //check if line is part of the response's text or it is a response header
          if(trim($lineArr[0])=="UnparsedText"){
          	$block["UnparsedText"][]=str_replace("\"","",$lineArr[1]);
         	}else{
          	$block[$lineArr[0]]=str_replace("\"","",$lineArr[1]);
          }
        }     
			}
  	}
  }
  //return
  $ret="";
  if(($match===true)&&(is_array($block))){
  	foreach($block as $k=>$v){
    	if(($k=="ActionID")||($k=="Server")) continue;
    	if(is_array($v)) $ret.=implode("\n",$v);
      else $ret.=$k.": ".$v."\n";
    }
  }
  return $ret;
}
// ---- Function ast_writeProxyUsers -------------------------------------------
/**
* PRIVATE: write astmanproxy.users file with one entry per clients
*
* @param void
*
* @return:int -> Always 1, not able to recognize errors
*/
function ast_writeProxyUsers(){
	global $db,$API_user,$API_pass,$voTools;
  //delete users file
  exec("sudo $voTools delAstProxyUsers",$output,$retScript);
  //add system access
  exec("sudo $voTools addAstProxyUser $API_user $API_pass",$output,$retScript);
  //extract users
  $users=getUser(0);
  if(is_array($users)){
    foreach($users as $user){
      if(intval($user["type"])!=USER_STANDARD) continue;
      if(trim($user["exten"])!="") $extension=$user["exten"];
      else $extension=$user["username"];
      $password=$user["password"];
      switch (intval($user["technology"])){
        case SIP:
          $channel="SIP/".$extension;
          break;
        case IAX2:
          $channel="IAX2/".$extension;
          break;
        default:
          $channel="";
          break;
      }
    	exec("sudo $voTools addAstProxyUser $extension $password $channel",$output,$retScript);
    }
  }
  return 1;
}
?>