<?
/**
 * VoiceOne Webservices Interface
 *
 * @package voiceone_webservices
 * @subpackage  interface
 */

//******************************************************************************
//******************               ENTITY                   ********************
//******************************************************************************

// ---- getEntity(id:int):entity[] ---------------------------------------------
/**
* Get the entities of VoiceOne. An Entity is a "VoiceOne object", such as extensions,
* offices, providers, trunks, and so on. The entities can be grouped in groups.
* The information given are the ID and the TYPE of entities. Nothing else.
*
* @param integer $id The entity ID or null if you want to get all the entities
* @return entityArray It returns a list of entities or the entity requested as describled in the wsdl structure (structs.php)
*/
function getEntity($id) {
  global $db;
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_entity $condition";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $ret[]=$arr;
  }
  return $ret;
}

// ---- putEntity(ent:entity):int ----------------------------------------------
/**
* Insert an entity in the VoiceOne system. Usually this is a internal method,
* which is called inside other method. Use this function when you want to manually
* add a new object (i.e. queue). Before doing it, you have to create a new
* entity and then, with the resulting ID, add a new entry in the queues table
* with the same ID.
*
* @param Entity $ent It accept an Entity type variable as describled in the wsdl structure (structs.php)
* @return integer $id It returns the ID of the new entity
*/
function putEntity($ent) {
  global $db;
  $type=$ent["type"];
  if ($ent["id"]!="") {
    $id=$ent["id"];
    $oldentity=getEntity($id);
    if (!isset($oldentity[0])) $qry="INSERT INTO cw_entity (`id`,`type`) VALUES ($id, $type)";
    else $qry="UPDATE cw_entity SET `type`=$type WHERE id=$id";
  }
  else $qry="INSERT INTO cw_entity (`type`) VALUES ($type)";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0;
  else if ($ent["id"]!="") $ret=$ent["id"];
    else $ret=$db->Insert_ID();
  //if asterisk is ON, reload it
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");
  //return
  return $ret;
}

// ---- delEntity(id:int):int --------------------------------------------------
function delEntity($id) {
  global $db;

  $qry="DELETE FROM cw_entity WHERE id=$id";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0; else $ret=1;
  return $ret;
}

// ---- getEntityName(id:int):string ----------------------------------------------
function getEntityName($entity_id) {
  $entity=getEntity($entity_id);
  $entityType=$entity[0]["type"];
  switch ($entityType) {
    case EXTENSION:
      $entityArray=getClient($entity_id,0);
      $entityName=$entityArray[0]["extension"];
    break;
    case PROVIDER_ACCOUNT:
      $entityArray=getProviderAccount($entity_id,0);
      $entityName=$entityArray[0]["name"];
    break;
    case OFFICE:
      $entityArray=getOffice($entity_id);
      $entityName=$entityArray[0]["name"];
    break;
    case PHONENUMBER:
      $entityArray=getPhoneNumber($entity_id);;
      $entityName=$entityArray[0]["name"];
    break;
    case MISDN_GROUP:
      $g=getIsdnGroup($entity_id);
      $entityName=$g[0]["name"];
    break;
    case ZAPATA_GROUP:
      $g=getZapataGroup($entity_id);
      $entityName=$g[0]["name"];
    break;
    case APPLICATION:
      $g=getApplication($entity_id);
      $entityName="app-".$g[0]["name"];
    break;
    case QUEUE:
      $g=getQueue($entity_id);
      $entityName=$g[0]["name"];
    break;
    case CHANNEL:
      $entityName="trunk_out_".$entity_id;
    break;
  }
  return $entityName;
}

// ---- getMetaGroupFromEntity(id:int):int ----------------------------------------------
function getMetaGroupFromEntity($entity_id) {
  $entity=getEntity($entity_id);
  $entityType=$entity[0]["type"];
  switch ($entityType) {
    case EXTENSION:
      $ret=GROUP_OF_EXTENSION;
    break;
    case PROVIDER_ACCOUNT:
    case ZAPATA_GROUP:
    case MISDN_GROUP:
    case ZAPATA_GROUP:
    case CHANNEL:
      $ret=GROUP_OF_RESOURCE;
    break;
    case OFFICE:
      $ret=GROUP_OF_OFFICE;
    break;
    case PHONENUMBER:
      $ret=GROUP_OF_PHONENUMBER;
    break;
    case APPLICATION:
      $ret=GROUP_OF_APPLICATION;
    break;
  }
  return $ret;
}

// ---- getMetaGroupFromGroup(id:int):int ----------------------------------------------
function getMetaGroupFromGroup($group_id) {
  $qry="SELECT * FROM `cw_group` WHERE `id`=".$group_id;
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $ret=$arr["type"];
  }
  return $ret;
}

// ---- addEntityToGroup(entity_id:int, extensions:ExtensionArray, groups:intArray):int

function addEntityToGroup($entity_id,$groups) {
  global $db;
  $ret=1;

  //get the Entity Name that is the context to be included
  $context=getEntityName($entity_id);


  //Add the entity to the meta group
  $metaGroup=getMetaGroupFromEntity($entity_id);
  if ($metaGroup!=GROUP_OF_OFFICE) {
    $groupArr=getGroup($metaGroup);
    $includeContext=$groupArr[0]["name"];
    $res=putAstConfig("extensions.conf", $includeContext, "include", $context);
    if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  }

  //add the metagroup to the group list
  $groups[]=$metaGroup;

  //add the extensions in extensions_table
  //add the entity to the groups into the cw_group_entity
  if (is_array($groups))
  foreach ($groups as $group_id) {

    $qry="SELECT * FROM cw_group_entity WHERE `group_id`=$group_id AND `entity_id`=$entity_id";
    $res=$db->Execute($qry);
    if ($res->RecordCount()==0) {
      $qry="INSERT INTO cw_group_entity (`group_id`,`entity_id`) VALUES ($group_id, $entity_id)";
      $res=$db->Execute($qry);
      if ($res===false) return false;
    }
  }

  //create the global var in the Ast_Config
  generateGlobalVar($groups);

  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  return $ret;
}

// ---- removeEntityFromGroup(entity_id: int):int

function removeEntityFromGroup($entity_id) {
  global $db;

  $entityName=getEntityName($entity_id);

  //remove the entity from the relations between entities and groups
  $db->Execute("DELETE FROM cw_group_entity WHERE entity_id=$entity_id");

  if (trim($entityName)!="") {
    //remove the entry from the ast_config
    //$res=delAstConfig("extensions.conf",$entityName,"switch");
    //$res=delAstConfig("extensions.conf",$entityName,"exten");
    //$res=delAstConfig("extensions.conf",$entityName,"include",$entityName);

    //delete the extensions from the extensions_table
    //$db->Execute("DELETE FROM extensions_table WHERE context='$entityName'");

    $res=delAstConfig("extensions.conf","%","include",$entityName);
  }

  //regenerate the global var from the ast config
  //generateGlobalVar();

  //$ret=delEntity($entity_id);
  return $ret;
}

//******************************************************************************
//******************              EXTENSION                 ********************
//******************************************************************************

// ---- getExtension(id:int):Extension[] ---------------------------------------
function getExtension($id){
  global $db;
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_extension $condition";
  $res=$db->Execute($qry);
  $ret=array();
  while ($v=$res->FetchRow()) $ret[]=$v;
  return $ret;
}

// ---- getExtensionByExtension(exten:string):Extension[] ----------------------
function getExtensionByExtension($exten){
  global $db;
  if ($exten!="") $condition=" WHERE extension='".string2sql($exten)."' ";
  $qry="SELECT * FROM cw_extension $condition";
  $res=$db->Execute($qry);
  $ret=array();
  while ($v=$res->FetchRow()) $ret[]=$v;
  return $ret;
}

// ---- putExtension(extension:Extension):int ----------------------------------
function putExtension($extension){
  global $db;
  $entity_id=$extension["entity_id"];
  $exten=$extension["extension"];
  if(($entity_id==0)or($exten=="")) return 0;
  $qry="INSERT INTO cw_extension (id,entity_id,extension) ";
  $qry.="VALUES (NULL, ".$entity_id.", '".string2sql($exten)."')";
  $res=$db->Execute($qry);
  if ($res===false) return 0;
  else return 1;
}

// ---- delExtension(id:int):int -----------------------------------------------
function delExtension($id){
  global $db;
  $qry="DELETE FROM cw_extension WHERE id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return 0;
  else return 1;
}
// ---- delExtensionByExtension(exten:string):int ------------------------------
function delExtensionByExtension($exten){
  global $db;
  $qry="DELETE FROM cw_extension WHERE extension='".string2sql($exten)."'";
  $res=$db->Execute($qry);
  if ($res===false) return 0;
  else return 1;
}
// ---- delExtensionByEntity(id:int):int -----------------------------------------------
function delExtensionByEntity($id){
  global $db;
  $qry="DELETE FROM cw_extension WHERE entity_id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return 0;
  else return 1;
}

// ---- getNextSpeedDial():int -------------------------------------------------
//private
function getNextSpeedDial() {
  global $db;
  //get exsisting speedDials
  $allExtensions=getExtension(0);
  if(is_array($allExtensions)){
    foreach($allExtensions as $e) $speedDials[]=$e["extension"];
  }
  //return the first available speedDial
  $next=1;
  $nextSD=$next."*";
  if(is_array($speedDials)){
    while(in_array($nextSD,$speedDials)){
      $next++;
      $nextSD=$next."*";
    }
    return $nextSD;
  }
  else return "1*";
}

//******************************************************************************
//******************               CLIENT                   ********************
//******************************************************************************

// ---- getClientAccount(id:int, technology:int):client[] ----------------------
function getClientAccount($id,$technology=0) {
  $clients=getClient($id,$technology);
  if(is_array($clients)){
  	foreach($clients as $client){
  	  if(isset($client["password"])) $client["password"]="";
  	  if(isset($client["voicemail"]["password"])) $client["voicemail"]["password"]="";
  	  $ret[]=$client;
    }
  }else if($id!=0){
    $virtual=getVirtualExtension($id);
    if((is_array($virtual))&&(is_array($virtual[0]))){
      $ret[0]=array(
          'id'          => $virtual[0]["id"],
          'name'        => $virtual[0]["name"],
          'surname'     => $virtual[0]["surname"],
          'extension'   => $virtual[0]["extension"],
          'alias'       => "",
          'username'    => $virtual[0]["username"],
          'password'    => "",
          'language'    => $virtual[0]["language"],      
          'groups'      => $virtual[0]["groups"],
          'technology'  => "",
          'voicemail'   => $virtual[0]["voicemail"],
        );
    }
  }
  return $ret;
}

// ---- putClientAccount(client:Client,oldpwd:String,newpwd:String):int --------
function putClientAccount($id,$tech,$language,$oldpwd="",$newpwd="",$vmail="",$voldpwd="",$vnewpwd=""){
  if(!(isset($id)and($id!=""))) return 0;
  //extract existent client's definition
  $clients=getClient($id,$tech);
  if(is_array($clients)){
    $client=$clients[0];
    //set new password
    if(($oldpwd!="")&&($newpwd!="")){
      if($oldpwd==$client["password"]) $client["password"]=$newpwd;
      else return -1;
    }
    //set new language
    if($language!="") $client["language"]=$language;
    //set new voicemail
    if (count($client["voicemail"])>0) {
      $client["voicemail"]["email"]=$vmail;
      if(($voldpwd!="")&&($vnewpwd!="")){
        if($voldpwd==$client["voicemail"]["password"]) $client["voicemail"]["password"]=$vnewpwd;
        else return -2;
      }
    }
    //save new client definition
    $ret=putClient($client);
  }else{
    $virtual=getVirtualExtension($id);
    if((is_array($virtual))&&(is_array($virtual[0]))){
      $virtual=$virtual[0];
      //set new password
      if(($oldpwd!="")&&($newpwd!="")){
        if($oldpwd==$virtual["password"]) $virtual["password"]=$newpwd;
        else return -1;
      }
      //set new language
      if($language!="") $virtual["language"]=$language;
      //set new voicemail
      if(count($virtual["voicemail"])>0) {
        $virtual["voicemail"]["email"]=$vmail;
        if(($voldpwd!="")&&($vnewpwd!="")){
          if($voldpwd==$virtual["voicemail"]["password"]) $virtual["voicemail"]["password"]=$vnewpwd;
          else return -2;
        }
      }
      //save new client definition
      $ret=putVirtualExtension($virtual);
    }
  }
  //return
  return $ret;
}

// ---- getClient(id:int, technology:int):client[] -----------------------------
function getClient($id,$technology=0) {
  global $db;
  $ret=array();
  if ($id!="") $condition=" WHERE id=$id ";
  else if ($technology!=0) $condition=" WHERE technology=$technology";
  $qry="SELECT * FROM cw_client $condition ORDER BY extension";
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) {
    //extract some variables
    $id=$v["id"];
    $extension=$v["extension"];
    //retrieve the client specific options
    $v["options"]=getEntityOption($id);
    //retrieve the client groups
    $qry="SELECT * FROM cw_group_entity  WHERE entity_id=$id";
    $resGrp=$db->Execute($qry);
    while ($arr=$resGrp->FetchRow()) $v["groups"][]=$arr["group_id"];
    //retrieve the voicemail
    $voicemail=getVoiceMail($extension);
    $v["voicemail"]=$voicemail[0];
    //retrieve the language
    $qry="SELECT * FROM cw_user WHERE entity_id=$id";
    $resUsr=$db->Execute($qry);
    while ($arr=$resUsr->FetchRow()) $v["language"]=$arr["lang"];
    //get location options
    $v["location"]=0;
    $v["area_code"]="";
    $channel=getChannel(getChannelFromEntity($id));
    if((is_array($channel))&&(is_array($channel[0]))){
      $v["location"]=$channel[0]["location"];
      $v["area_code"]=$channel[0]["area_code"];
    }
    //define ret
    $ret[]=$v;
  }
  return $ret;
}

// ---- putClient(client:Client):int -------------------------------------------
function putClient($client) {
  global $db,$realm,$API_PROXY_enabled,$asteriskAppDelimeter;
  $entity=array();
  //check if extension is numeric
  if (!is_numeric($client['extension'])) return 0;
  //if already exists, delete it
  if (isset($client["id"]) and $client["id"]!="") {
    $channel["id"]=getChannelFromEntity($client["id"]);
    //get related agent
    $old_agentId=0;
    $old_agentId=getAgentFromEntity($client["id"]);
    if($old_agentId!=0){
    	$old_agentArr=getAgent($old_agentId);
    	$agent["id"]=$old_agentArr[0]["id"];
    	$agent["joined_queues"]=$old_agentArr[0]["joined_queues"];
    }
    //retrive the already binded user, if exists, to maintain the old user id and type
    $resUsr=$db->Execute("SELECT * FROM cw_user WHERE entity_id=".$client["id"]);
    while ($arru=$resUsr->FetchRow()) {
      $userId=$arru["id"];
      $userType=$arru["type"];
    }
    //retrieve the provisioning options
    $oldProvClient=getProvClientFromEntity($client["id"]);
    //delete client
    delClient($client["id"],false);
    $entity["id"]=$client["id"];
  }
  //create a new entity
  $entity["type"]=EXTENSION;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  //extract the rule set ID
  $rule=extractRuleFromOption($client["options"],$client["technology"]);
  //insert the client credentials
  $qry="INSERT INTO cw_client (id,name,surname,extension,alias,username,password,rule,technology) ";
  $qry.="VALUES ($entity_id,'".string2sql($client['name'])."','".string2sql($client['surname'])."',";
  $qry.="'".string2sql($client['extension'])."','".string2sql($client['alias'])."','".string2sql($client['username'])."',";
  $qry.="'".string2sql($client['password'])."',$rule,".$client['technology'].")";
  $res=$db->Execute($qry);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  //manage per-technology options
  switch($client["technology"]){
    case MISDN:
      //extract port option and set channel resource
      $portsOptionArr=getOptionByName("ports","mISDN");
      $portsOptionId=$portsOptionArr[0]["id"];
      foreach ($client["options"] as $k=>$v){
        if($v["key"]==$portsOptionId) $port=$v["value"];
      }
      $resource=$port."/".$client["extension"];
      break;
    case ZAP:
      //extract channel option and set channel resource
      $channelOptionArr=getOptionByName("channel","ZAP");
      $channelOptionId=$channelOptionArr[0]["id"];
      foreach ($client["options"] as $k=>$v){
        if($v["key"]==$channelOptionId) $zchannel=$v["value"];
      }
      $resource=$zchannel; //$resource=$zchannel."/".$client["extension"];
      //extract signalling and set it in otheroption array
      $signalling="";
      if((function_exists(getZapataChannel))&&($zchannel!=0)){
        $ret=getZapataChannel(0);
        if(is_array($ret)){
          foreach($ret as $c){
            if($c["zchannel"]==$zchannel){
              $signalling=$c["signalling"];
              break;
            }
          }
        }
      }
      $client["signalling"]=$signalling;
      break;
    default:
      $resource=$client["extension"];
      break;
  }
  //write the options in the asterisk table
  putEntityOption($client["options"], $client["technology"], $entity_id, $client);
  $ret=1;
  //get the tech infos
  $techArr=getTechnology($client["technology"]);
  $techName=$techArr[0]["name"];
  $techVoip=$techArr[0]["voip"];
  //set some variables
  $context=$client["extension"];
  $extension=$client["extension"];
  $alias=$client["alias"];
  $channelName=$techName."/".$extension;
  //put the new Channel
  $channel['technology']=$client["technology"];
  $channel['resource']=$resource;
  $channel["rule"]=$rule;
  $channel['key']=$entity_id;
  $channel["location"]=$client["location"];
  $channel["area_code"]=$client["area_code"];
  $channelId=putChannel($channel);
  //help text for viewAstConfig
  putAstConfig("extensions.conf", $context, "", "", $extension." - ".$client["name"]." ".$client["surname"]." - ".$channelName,1);
  //Hint
  putAstConfig("extensions.conf", $context, "exten", "$extension,hint,$channelName");
  putAstConfig("extensions.conf", $context, "exten", "$extension,1,AGI(dial.php".$asteriskAppDelimeter."entity=$entity_id&group=".GROUP_OF_EXTENSION."&extension=$extension)");
  if($alias!="") putAstConfig("extensions.conf", $context, "exten", "$alias,1,Goto,$extension".$asteriskAppDelimeter."1");
  //add the entity to the selected groups
  $res=addEntityToGroup($entity_id, $client["groups"]);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  //return value
  if($ret==1) $ret=$entity_id; else $ret=0;
  //insert a new user for the web administration
  if($ret>0) putUser(array("id"=>$userId,"password"=>$client['password'],"type"=>$userType, "entity_id"=>$entity_id, "lang"=>$client["language"]));
  //insert the voicemail
  if (isset($client["voicemail"])) $ret=putVoiceMail($client["voicemail"]);
  //insert entry in cw_extension
  if($ret>0){
    $entry=array();
    $entry["id"]=0;
    $entry["entity_id"]=$entity_id;
    $entry["extension"]=$client['extension'];
    $ret=putExtension($entry);
		if($client["alias"]!=""){
      $entry=array();
      $entry["id"]=0;
      $entry["entity_id"]=$entity_id;
      $entry["extension"]=$client['alias'];
      $ret=putExtension($entry);
		}
  }
  //create associated agent
  if($ret>0){
    $agent["client"]=$entity_id;
    $agent["extension"]=$client['extension'];
    $agent["username"]="";
    $agent["password"]="";
    putAgent($agent);
  }
  //create provisioving entry
  if($ret>0){
    if(is_array($oldProvClient)) $ret=putProvClient($oldProvClient);
    else $ret=touchProvClient(0,$entity_id);
  }
  //overwrite linked virtual extension
  if($ret>0){
  	$virtual=getVirtualExtensionFromClient($client["id"]);
  	if(($virtual!=0)&&(is_array($virtual))){
    	$ret=putVirtualExtension($virtual);
    }
  }
  //if asterisk is ON, reload extensions and tech driver
  $status=pbxSwitch("status");
  if(($ret>0)and($status=="ON")){
    //reload technology driver
    switch($client["technology"]){
      case MISDN:
        if(function_exists(reloadmISDNDriver)) reloadmISDNDriver();
        break;
      case ZAP:
        if(function_exists(reloadZapDriver)) reloadZapDriver();
        break;
    }
    //send reload
    sendCommand("reload");
  }
  //set return
  return $ret;
}

// ---- delClient(id:int):int --------------------------------------------------
function delClient($entity_id, $reload=true) {
  global $db,$API_PROXY_enabled;
  //retrieve the tech type from the client
  $qry="SELECT * FROM cw_client WHERE id='".string2sql($entity_id)."'";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $extension=$arr["extension"];
  $alias=$arr["alias"];
  $tech=$arr["technology"];
  $name=$arr["extension"];
  //remove client specific options
  $ret=delEntityOption($entity_id,$tech);
  //delete the Channel
  $channelId=getChannelFromEntity($entity_id);
  if ($channelId!=0) delChannel($channelId);
  //remove the client from the auxiliary table
  $qry="DELETE FROM cw_client WHERE id=".$entity_id;
  $db->Execute($qry);
  //remove the user from the user table, so it delete the account to voiceone
  $user_id=getUserIdFromEntity($entity_id);
  if($user_id!=0){
    if($reload===true){
      delUser($user_id);
    }else{
      delUser($user_id,false);
    }
  }
  //remove the entry from the ast_config
  if($name!="") $res=delAstConfig("extensions.conf",$name);
  //delete the extensions from the extensions_table
  $db->Execute("DELETE FROM extensions_table WHERE context='$name'");
  //remove all the client inclusions
  if(trim($name)!="") $res=delAstConfig("extensions.conf","%","include",$name);
  //remove the client from the asterisk tables and groups
  $ret=removeEntityFromGroup($entity_id);
  //remove the entity
  $ret=delEntity($entity_id);
  //delete the VoiceMail
  $ret=delVoiceMail($name);
  //delete associated agent
  $ret=delAgent(getAgentFromEntity($entity_id));
  //delete from cw_extension
  $ret=delExtensionByExtension($extension);
  if(trim($alias)!="") $ret=delExtensionByExtension($alias);
  //delete provisioning client
  $provClient=getProvClientFromEntity($entity_id);
  if(is_array($provClient)) $ret=delProvClient($provClient["id"]);
  if($reload==true){
    //if asterisk is ON, reload extensions
    $status=pbxSwitch("status");
    if($status=="ON"){
      //reload technology driver
      switch($tech){
        case MISDN:
          if(function_exists(reloadmISDNDriver)) reloadmISDNDriver();
          break;
        case ZAP:
          if(function_exists(reloadZapDriver)) reloadZapDriver();
          break;
      }
      //send reload
      sendCommand("reload");
    }
  }
  return $ret;
}

// ---- gelClientFromExtension(extension:string):client ------------------------
function gelClientFromExtension($extension) {
  global $db;
  $ret=0;
	$clients=getClient(0,0);
  if((is_array($clients))&&(count($clients)>0)){
  	foreach($clients as $client){
      if($extension==$client["extension"]){
  		  $ret=$client;
        break;
      }
  	}
  }
  return $ret;
}

//******************************************************************************
//*******************               CID                   **********************
//******************************************************************************

// ---- getCid(trunk_id:int, ext_id:int):Cid[] ---------------------------------
function getCid($trunk_id=0,$ext_id=0) {
  global $db;
  $ret=array();
  if ($trunk_id!=0) $condition=" WHERE trunk=$trunk_id ";
  else if ($ext_id!=0) $condition=" WHERE extension=$ext_id";
  $qry="SELECT * FROM cw_cid $condition ORDER BY extension";
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) $ret[]=$v;
  return $ret;
}

// ---- putCid(exten_id:int,cid:Cid[]):int -------------------------------------
function putCid($exten_id,$cidArr) {
  global $db;
  //delete old cid settings
  $ret=delCid(0,$exten_id);
  if($ret!=0){
    //insert new cid
    if(is_array($cidArr)){
    	foreach($cidArr as $cid){
    		$trunk_id=$cid["trunk"];
    		$clir=$cid["clir"];
        $cid_number=$cid["cid_number"];
    		$cid_name=$cid["cid_name"];
    		$qry="INSERT INTO cw_cid (trunk,extension,clir,cid_number,cid_name) ";
      	$qry.="VALUES ($trunk_id,$exten_id,$clir,'".string2sql($cid_number)."','".string2sql($cid_name)."')";
      	$res=$db->Execute($qry);
      	if($res===false) $ret=$ret and 0; else $ret=$ret and 1;
    	}
  		return $ret;
  	}else return 0;
  }else return 0;
}

// ---- delCid(trunk_id:int, ext_id:int):int -----------------------------------
function delCid($trunk_id=0,$ext_id=0) {
  global $db;
  if ($trunk_id!=0) $condition=" WHERE trunk=$trunk_id ";
  else if ($ext_id!=0) $condition=" WHERE extension=$ext_id";
  $qry="DELETE FROM cw_cid $condition";
  $res=$db->Execute($qry);
  if($res===false) return 0; else return 1;
}
// ---- getCidFromTrunk(trunk_id:int):Cid[] ---------------------------------
function getCidFromTrunk($trunk_id) {
  global $db;
  if(($trunk_id==0)||(trim($trunk_id)=="")) return 0;
  $channel_id=getChannelFromEntity($trunk_id);
	if(($channel_id==0)||(trim($channel_id)=="")) return 0;
  $qry="SELECT * FROM cw_cid WHERE trunk=".$channel_id." ORDER BY extension";
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) $ret[]=$v;
  return $ret;
}
// PRIVATE ---- pruneCidClient(trunk_id:int, numberArr:Array):int --------------
function pruneCidClient($trunk_id,$numberArr){
  global $db;
  //check input values
  if(($trunk_id==0)||(trim($trunk_id)=="")) return 0;
  //delete cid setting linked to a number which is not in the list
  if(is_array($numberArr)){
    $notInList="";
  	foreach($numberArr as $number){
    	$notInList.="'".$number."',";
    }
    if(strlen($notInList)<2) return 0;
    $notInList=substr($notInList,0,-1);
  	$qry="DELETE FROM cw_cid WHERE trunk='".$trunk_id."' AND cid_number NOT IN (".$notInList.") ";
  }else{
  	$qry="DELETE FROM cw_cid WHERE trunk='".$trunk_id."' ";
  }
  $res=$db->Execute($qry);
  if($res===false) return 0; else return 1;
}
//******************************************************************************
//******************               OFFICE                   ********************
//******************************************************************************

// ---- getOffice(id:int):Office[] ---------------------------------------------
function getOffice($id) {
  global $db;
  //Retrieve Office
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_office ".$condition." ORDER BY name";
  $res=$db->Execute($qry);
  while($v=$res->FetchRow()) {
    //set alias
    if($v["alias"]!="") $v["alias"]=explode("|",$v["alias"]);
    else $v["alias"]=array();
    //retrieve the office specific options
    $v["options"]=getEntityOption($id);
    //retrieve the speed dial
    $channel=getChannel(getChannelFromEntity($v["id"]),0);
    $v["speeddial"]=substr($channel[0]["speeddial"],0,-1);
    //retrieve location settings
    $v['location']=$channel[0]["location"];
    $v['area_code']=$channel[0]["area_code"];
    $v['prefix_policy']=$channel[0]["prefix_policy"];
    $v['prefix_format']=$channel[0]["prefix_format"];
    //retrieve the phone numbers
    $v["numbers"]=$channel[0]["cid_list"];
    $v["did"]=$channel[0]["did"];
    //retrieve CID settings
    $v["enable_cid"]=$channel[0]["enable_cid"];
    $v["force_cid"]=$channel[0]["force_cid"];
    $v["clir"]=$channel[0]["clir"];
    $v["cid_number"]=$channel[0]["cid_number"];
    $v["cid_name"]=$channel[0]["cid_name"];
    $v["cid_appexten"]=$channel[0]["cid_appexten"];
    //retrieve the office groups
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=".$v["id"];
    $resg=$db->Execute($qry);
    while ($arr=$resg->FetchRow()) $v["groups"][]=$arr["group_id"];
    //prepare ruturn struncture
    $ret[]=$v;
  }
  return $ret;
}

// ---- putOffice(dep:Office):int ---------------------------------------------
function putOffice($dep) {
  global $db,$voiceoneWSDocumentRoot,$voTools,$keyName;
  //If already exists, retrieve information and delete it
  $entity=array();
  if (isset($dep["id"]) and $dep["id"]!="") {
    $old_channel=getChannel(getChannelFromEntity($dep["id"]));
    $channel["id"]=$old_channel[0]["id"];
    $channel["order"]=$old_channel[0]["order"];
    delOffice($dep["id"],false);
    $entity["id"]=$dep["id"];
  }
  //Create a new entity
  $entity["type"]=OFFICE;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  //Extract the rule set Id
  $rule=extractRuleFromOption($dep["options"],IAX2);
  //Insert the client credentials
  $name=$dep["name"];
  $label=$dep["label"];
  $username=$dep["username"];
  $password=$dep["password"];
  $inkey=$dep["inkey"];
  $host=$dep["host"];
  $port=$dep["port"];
  $register=$dep["register"];
  $alias=msns2string($dep["alias"]);
  $qry="INSERT INTO cw_office (id,name,label,username,password,inkey,host,port,register,alias,rule) ";
  $qry.="VALUES ($entity_id,'".string2sql($name)."','".string2sql($label)."','".string2sql($username)."','".string2sql($password)."',";
  $qry.="'".string2sql($inkey)."','".string2sql($host)."','".string2sql($port)."','".string2sql($register)."','".string2sql($alias)."',$rule)";
  $res=$db->Execute($qry);
  if ($res===false) return 0;
  //Write the options in the asterisk table
  putEntityOption($dep["options"], IAX2, $entity_id, $dep);
  //Put the Channel
  $channel["technology"]=IAX2;
  $channel["resource"]=$dep["name"];
  $channel["rule"]=$rule;
  $channel['key']=$entity_id;
  $channel['speeddial']=$dep["speeddial"];
  $channel['trunk']=1;
  $channel['did']=$dep["did"];
  if($dep["did"]==1) $channel['msns']=Array("X"); else $channel['msns']=null;
  $channel['enable_cid']=$dep["enable_cid"];
  $channel['cid_list']=$dep["numbers"];
  $channel['cid_name']=$dep["cid_name"];
  $channel['cid_number']=$dep["cid_number"];
  $channel['location']=$dep["location"];
  $channel['area_code']=$dep["area_code"];
  $channel['prefix_policy']=$dep["prefix_policy"];
  $channel['prefix_format']=$dep["prefix_format"];
  if($dep["cid_appexten"]=="") $dep["cid_appexten"]=0; $channel['cid_appexten']=$dep["cid_appexten"];
  if($dep["force_cid"]=="") $dep["force_cid"]=0; $channel['force_cid']=$dep["force_cid"];
  if($dep["clir"]=="") $dep["clir"]=0; $channel['clir']=$dep["clir"];
  $channelId=putChannel($channel);
  //Add the entity to the selected groups
  $res=addEntityToGroup($entity_id, $dep["groups"]);
  if ($res===false) $ret=0; else $ret=1;
  //Get the tech infos
  $techArr=getTechnology(IAX2);
  $techRealTime = $techArr[0]["realtime"];
  $techTable    = $techArr[0]["table"];
  $techFile     = $techArr[0]["config_file"];
  //Update the incoming context
  if ($techRealTime==1) {
    $qry="UPDATE $techTable set context='".string2sql("trunk_".$channelId)."' WHERE name='".string2sql($dep["name"])."'";
    $db->Execute($qry);
  } else {
    $res=delAstConfig($techFile, $dep["name"], "context");
    $res=putAstConfig($techFile, $dep["name"], "context", "trunk_".$channelId);
  }
  //Register the provider
  if ($register=="1" and $host!="") {
    if ($password!="") putAstConfig($techFile, "general", "register", $dep["username"].":".$dep["password"]."@".$dep["host"]);
    else if ($inkey!="") putAstConfig($techFile, "general", "register", $dep["username"].":[".$keyName."]@".$dep["host"]);
    else putAstConfig($techFile, "general", "register", $dep["username"]."@".$dep["host"]);
  }
  //Insert numbers in the ast_config
  $context="trunk_out_".$channelId;
  if (is_array($dep["alias"]))
  foreach ($dep["alias"] as $n) {
    putAstConfig("extensions.conf",$context,"exten",$n.",1,AGI(dial.php,entity=$entity_id&group=".GROUP_OF_OFFICE."&extension=".$n.")");
  }
  /*
  //insert the office context
  $context=$dep["name"];
  $channelId=getChannelFromEntity($entity_id);
  if (asteriskRealtime===true)
    putExtension(Array("context" => $context, "exten"=>"s", "priority"=>1, "app"=>"include",  "appdata"=>"trunk_out_".$channelId));
  else
    putAstConfig("extensions.conf",$context,"exten","s,1,include(trunk_out_".$channelId.")");
  */
  //Insert phonenumbers in the cw_extension
  if (is_array($dep["alias"]))
  foreach ($dep["alias"] as $n) {
    $entry=array();
    $entry["entity_id"]=$entity_id;
    $entry["extension"]=$n;
    $res=putExtension($entry);
  }
  //Create the inkey file in the sandbox
  $inkeyFilename=$dep["name"];
  file_put_contents ( $voiceoneWSDocumentRoot."/sandbox/".$inkeyFilename.".pub", $dep["inkey"] );
  //Move the office rsa key in the proper location
  exec("sudo $voTools inkey \"$voiceoneWSDocumentRoot/sandbox/$inkeyFilename\"",$output,$retScript);
  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") {
    sendCommand("init keys");
    sendCommand("reload");
  }
  //Set return value
  return $entity_id;
}

// ---- delOffice(id:int):int -------------------------------------------------
function delOffice($entity_id,$reload=true) {
  global $db;
  //retrieve office information
  $resArr=getOffice($entity_id);
  $office=$resArr[0];
  //Remove the office from the asterisk tables and groups
  $ret=removeEntityFromGroup($entity_id);
  //Remove office specific options
  $ret=delEntityOption($entity_id,IAX2);
  //Delete the Channel
  $channelId=getChannelFromEntity($entity_id);
  if(($reload===true)&&($channelId!=0)) delChannel($channelId);
  //Remove the office from voiceone table
  $qry="DELETE FROM cw_office WHERE id=".$entity_id;
  $res=$db->Execute($qry);
  if ($res===false) $ret=0; else $ret=1;
  //delete the registration of the provider
  if ($office["username"]!="")
    delAstConfig("iax.conf", "general", "register", $office["username"]."%" );
  /*
  //delete the office context
  if ($office["name"]!="") delAstConfig("extensions.conf", $office["name"], "include");
  */
  //delete speeddial from cw_extension
  delExtensionByExtension($office["speeddial"]);
  //delete alias from cw_extension
  delExtensionByEntity($entity_id);
  //remove the entity
  $ret=delEntity($entity_id);
  //if asterisk is ON, reload extensions
  if($reload===true) {
    $status=pbxSwitch("status");
    if($status=="ON") sendCommand("reload");
  }
  return $ret;
}

//******************************************************************************
//******************               PROVIDER                 ********************
//******************************************************************************

// ---- getProvider(id:int):Provider[] ----------------------------------------
function getProvider($id) {
  global $db;
  // get providers
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_provider $condition";
  $resProv=$db->Execute($qry);
  while ($v=$resProv->FetchRow()) {
    $id=$v["id"];
    $v["accounts"]=getProviderAccount(0,$id);
    $ret[]=$v;
  }
  return $ret;
}

// ---- getProviderAccount(id:int,provider:int):ProviderAccount[] -------------
function getProviderAccount($id,$provider) {
  global $db;
  // get accounts
  if ($id!="") $condition=" WHERE id=$id ";
  else if ($provider!="") $condition=" WHERE provider=$provider ";
  $qry="SELECT * FROM cw_provider_account $condition";
  $resAcc=$db->Execute($qry);
  while ($account=$resAcc->FetchRow()) {
    //retrieve the account specific options
    $account["options"]=getEntityOption($account["id"]);
    //retrieve the speed dial
    $channel=getChannel(getChannelFromEntity($account["id"]),0);
    $account["speeddial"]=substr($channel[0]["speeddial"],0,-1);
    //retrieve location settings
    $account['location']=$channel[0]["location"];
    $account['area_code']=$channel[0]["area_code"];
    $account['prefix_policy']=$channel[0]["prefix_policy"];
    $account['prefix_format']=$channel[0]["prefix_format"];
    //retrieve the phone numbers
    $account["numbers"]=$channel[0]["cid_list"];
    $account["did"]=$channel[0]["did"];
    //retrieve CID settings
    $account["enable_cid"]=$channel[0]["enable_cid"];
    $account["force_cid"]=$channel[0]["force_cid"];
    $account["clir"]=$channel[0]["clir"];
    $account["cid_number"]=$channel[0]["cid_number"];
    $account["cid_name"]=$channel[0]["cid_name"];
    $account["cid_appexten"]=$channel[0]["cid_appexten"];
    $ret[]=$account;
  }
  return $ret;
}

// ---- putProvider(res:Provider):int -----------------------------------------
function putProvider($res) {
  global $db;
  $provider_id=0;
  //if account already exists delete and create a new entry
  if (isset($res["id"]) and $res["id"]!="") {
    delProvider($res["id"],false);
    $provider_id=$res["id"];
  }
  //delete host if provider is dynamic
  if($res['type']==1) $res['host']="";
  //insert the provider credentials
  $qry="INSERT INTO cw_provider (id,name,type,host,port,technology) ";
  $qry.="VALUES ($provider_id,'".string2sql($res['name'])."','".string2sql($res['type'])."',";
  $qry.="'".string2sql($res['host'])."','".string2sql($res['port'])."',".$res['technology'].")";
  $ret=$db->Execute($qry);
  if ($ret===false) return 0;
  $qry="SELECT id FROM cw_provider ";
  $qry.="WHERE name='".string2sql($res['name'])."' ";
  $qry.="AND type='".string2sql($res['type'])."' ";
  $qry.="AND host='".string2sql($res['host'])."' ";
  $qry.="AND port='".string2sql($res['port'])."' ";
  $qry.="AND technology=".$res['technology'];
  $ret=$db->Execute($qry);
  if ($ret===false) return 0;
  else {
    $provider=$ret->FetchRow();
    return $provider["id"];
  }
}

// ---- putProviderAccount(res:ProviderAccount):int ---------------------------
function putProviderAccount($res) {
  global $db;
  //check provider and retrieve informations
  if (!(isset($res["provider"]) and $res["provider"]!="")) return 0;
  $providerArr=getProvider($res["provider"]);
  if (!(is_array($providerArr))) return 0;
  $provider=$providerArr[0];
  $res["p_name"]=$provider["name"];
  $res["technology"]=$provider["technology"];
  $res["type"]=$provider["type"];
  $res["host"]=$provider["host"];
  //$res["port"]=$provider["port"]; // Now an account has its own port
  //if account already exists delete and create a new entry
  $entity=array();
  if (isset($res["id"]) and $res["id"]!="") {
    $old_channel=getChannel(getChannelFromEntity($res["id"]));
    $channel["id"]=$old_channel[0]["id"];
    $channel["order"]=$old_channel[0]["order"];
    delProviderAccount($res["id"],false);
    $entity["id"]=$res["id"];
  }
  //create a new entity
  $entity["type"]=PROVIDER_ACCOUNT;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  //extract the rule set ID
  $rule=extractRuleFromOption($res["options"],$res["technology"]);
  //insert the provider credentials
  $qry="INSERT INTO cw_provider_account (id,name,provider,port,username,password,allow_in,contact_extension,rule) ";
  $qry.="VALUES ($entity_id,'".string2sql($res['name'])."','".$res['provider']."','".string2sql($res['port'])."','".string2sql($res['username'])."',";
  $qry.="'".string2sql($res['password'])."','".$res['allow_in']."','".string2sql($res['contact_extension'])."',$rule)";
  $r=$db->Execute($qry);
  if ($r===false) $ret=$ret and 0; else $ret=$ret and 1;
  //perform other action if it is a VoIP provider
  if ($res["technology"]>0) {
    //write the options in the asterisk table
    $otherOpt=$res;
    if($res["type"]==1){
      $otherOpt["host"]="dynamic";
      $otherOpt["fromdomain"]="null"; 
      $otherOpt["fromuser"]="null";
    }else{
      $otherOpt["fromdomain"]=$res["host"];
      $otherOpt["fromuser"]=$res["username"];
    }
    putEntityOption($res["options"], $res["technology"], $entity_id, $otherOpt);
    //extract the phone number
    if (is_array($res["numbers"])) $numbers=implode("|",$res["numbers"]);
    else $numbers=array($res["username"]);
    //put the Channel
    $channel["technology"]=$res["technology"];
    $channel["resource"]=$res["name"];
    $channel["rule"]=$rule;
    $channel["msns"]=$res["numbers"];
    $channel['key']=$entity_id;
    $channel['speeddial']=$res["speeddial"];
    $channel['trunk']=1;
    $channel['did']=$res["did"];
    $channel['enable_cid'] = $res["enable_cid"];
    $channel['cid_list'] = $res["numbers"];
    $channel['cid_number'] = $res["cid_number"];
    $channel['cid_name'] = $res["cid_name"];
    $channel['cid_appexten'] = $res["cid_appexten"];
    $channel['force_cid'] = $res["force_cid"];
    $channel['clir'] = $res["clir"];
    $channel['location'] = $res["location"];
    $channel['area_code'] = $res["area_code"];
    $channel['prefix_policy'] = $res["prefix_policy"];
    $channel['prefix_format'] = $res["prefix_format"];
    $channelId=putChannel($channel);
    //include account in provider's context
    putAstConfig("extensions.conf", "provider_".$res["provider"], "include", "trunk_".$channelId);
    //get the tech infos
    $techArr=getTechnology($res["technology"]);
    $techRealTime = $techArr[0]["realtime"];
    $techTable    = $techArr[0]["table"];
    $techFile     = $techArr[0]["config_file"];
    //update the incoming context
    if($techRealTime==1) {
      //$qry="UPDATE $techTable set context='trunk_".$channelId."' WHERE name='".$res["name"]."'";
      $qry="UPDATE $techTable set context='".string2sql("provider_".$res["provider"])."' WHERE name='".string2sql($res["name"])."'";
      $db->Execute($qry);
      //register the provider account if host is not dynamic
      if($res["type"]!=1){
        $contactExt=$res["contact_extension"];
        if(($contactExt!="")&&($contactExt!="s")&&($contactExt!="S")) $contactExt="/".$contactExt;
        else $contactExt="";
        if((isset($res["allow_in"]))&&($res["allow_in"]==1)){
          $commentDesc="";
          $commented=0;
        }else{
          $commentDesc="register disabled by provider definition";
          $commented=1;
        }
        if ($res["technology"]==SIP)  putAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["name"].$contactExt,$commentDesc,$commented);
        if ($res["technology"]==IAX2) putAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["host"].$contactExt,$commentDesc,$commented);
      }
    }
    else {
      delAstConfig($techFile, $res["name"], "context");
      putAstConfig($techFile, $res["name"], "context", "trunk_".$channelId);
    }
  }
  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");
  //return entity id
  return $entity_id;
}

// ---- delProvider(id:int,reload:bool):int ---------------------------------
function delProvider($id,$reload=true) {
  global $db;
  $ret=1;
  //check id and get provider info
  if (!(isset($id) and $id!="")) return 0;
  $providerArr=getProvider($id);
  if (!(is_array($providerArr))) return 0;
  $provider=$providerArr[0];
  if($reload===true){
    // if recursive is true -> delete all provider's account
    $accountArr=$provider["accounts"];
    if(is_array($accountArr)){
      foreach($accountArr as $account){
        $r=delProviderAccount($account["id"]);
        if ($r>0) $ret=$ret and 1;
        else {
          $ret=$ret and 0;
          break;
        }
      }
    }
  }
  //remove the entry from the provider table
  if ($ret>0){
    $qry="DELETE FROM cw_provider WHERE id=$id ";
    $ret=$db->Execute($qry);
  }
  //if asterisk is ON, reload
  if ($ret>0){
    $status=pbxSwitch("status");
    if($status=="ON") sendCommand("reload");
  }
  //return
  return $ret;
}

// ---- delProviderAccount(id:int,reload:int):int -----------------------------
function delProviderAccount($entity_id,$reload=true) {
  global $db;
  //check id and get account info
  if (!(isset($entity_id) and $entity_id!="")) return 0;
  $resArr=getProviderAccount($entity_id,0);
  if (!(is_array($resArr))) return 0;
  $res=$resArr[0];
  //get provider info
  $providerArr=getProvider($res["provider"]);
  if (!(is_array($providerArr))) return 0;
  $provider=$providerArr[0];
  $res["technology"]=$provider["technology"];
  $res["host"]=$provider["host"];
  //get the tech infos
  $techArr=getTechnology($res["technology"]);
  $techRealTime = $techArr[0]["realtime"];
  $techTable    = $techArr[0]["table"];
  $techFile     = $techArr[0]["config_file"];
  //get contact extension
  $contactExt=$res["contact_extension"];
  if(($contactExt!="")&&($contactExt!="s")&&($contactExt!="S")) $contactExt="/".$contactExt;
  else $contactExt="";
  //remove client specific options
  $ret=delEntityOption($entity_id,$res["technology"]);
  //delete the Channel
  $channelId=getChannelFromEntity($entity_id);
  if(($reload===true)&&($channelId!=0)) delChannel($channelId);
  //remove the entry from the resource table
  $qry="DELETE FROM cw_provider_account WHERE id=".$entity_id;
  $db->Execute($qry);
  //delete the registration of the provider
  if ($res["technology"]==SIP)  delAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["name"].$contactExt);
  if ($res["technology"]==IAX2) delAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["host"]);
  //include account from provider's context
  delAstConfig("extensions.conf","provider_".$res["provider"],"include","trunk_".$channelId);
  //remove the resource from the asterisk tables
  $ret=delEntity($entity_id);
  //if asterisk is ON, reload extensions
  if ($reload==1) {
    $status=pbxSwitch("status");
    if($status=="ON") sendCommand("reload");
  }
  return $ret;
}

//******************************************************************************
//******************               CHANNEL                  ********************
//******************************************************************************

// ---- getTechnology(id:int):Technology[] -------------------------------

function getTechnology($id) {
  global $db;
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_technology $condition";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $ret[]=$arr;
  }
  return $ret;
}

// ---- getChannel(id:int, technology:int):ChannelArray ------------------------
function getChannel($id=0,$technology=0) {
  global $db;
  //get the technology name and compute the Channel name
  if ($id!="") $condition=" WHERE id=$id ";
  else if ($technology!="") $condition=" WHERE technology=$technology ";
  $qry="SELECT * FROM cw_channel ".$condition." ORDER BY `order`";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()){
    foreach($arr as $field=>$value) $v[$field]=$value;
    if($v["msns"]!="")  $v["msns"]=explode("|",$v["msns"]); else $v["msns"]=array();
    if($v["cid_list"]!="") $v["cid_list"]=explode("|",$v["cid_list"]); else $v["cid_list"]=array();
    $ret[]=$v;
  }
  return $ret;
}

// ---- getTrunk(id:int, technology:int):ChannelArray -----------------
function getTrunk($id=0,$technology=0) {
  global $db;
  //get the technology name and compute the Channel name
  $condition="WHERE trunk=1";
  if ($id!="") $condition.=" AND id=$id ";
  else if ($technology!="") $condition.=" AND technology=$technology ";
  $qry="SELECT * FROM cw_channel ".$condition." ORDER BY `order`";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $v["msns"]=explode("|",$v["msns"]);
    $v["cid_list"]=explode("|",$v["cid_list"]);
    $ret[]=$v;
  }
  return $ret;
}

// ---- getChannelFromEntity(id_entity:int):id -----------------
//private
function getChannelFromEntity($id_entity) {
  global $db;
  $qry="SELECT * FROM cw_channel WHERE `key`=$id_entity";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    $arr=$res->FetchRow();
    $ret=$arr["id"];
  } else $ret=0;
  return $ret;
}

// ---- moveTrunk(id:int, direction: int):int ----------------------------------
// (Direction=1 : up  |  Direction=2: down)
function moveTrunk($id,$direction=1,$top=0) {
  global $db;
  //if top!=0 calculate rounds
  if(($top==1)and($direction==1)){
    //retrieve the current order number
    $qry="SELECT `order` FROM cw_channel WHERE id='$id'";
    $res=$db->Execute($qry);
    if($res!==false){
      $arr = $res->FetchRow();
      $orderFrom=$arr["order"];
      //count rounds
  		$qry="SELECT count(*) as count_trunk FROM cw_channel WHERE `order` < $orderFrom and trunk=1";
      $res=$db->Execute($qry);
      if($res!==false){
        $arr=$res->FetchRow();
        $rounds=$arr["count_trunk"];
      }else return 0;
    }else return 0;
  } else if(($top==1)and($direction==2)){
    //retrieve the current order number
    $qry="SELECT `order` FROM cw_channel WHERE id='$id'";
    $res=$db->Execute($qry);
    if($res!==false){
      $arr = $res->FetchRow();
      $orderFrom=$arr["order"];
      //count rounds
      $qry="SELECT count(*) as count_trunk FROM cw_channel WHERE `order` > $orderFrom and trunk=1";
      $res=$db->Execute($qry);
      if($res!==false){
        $arr=$res->FetchRow();
        $rounds=$arr["count_trunk"];
      }else return 0;
    }else return 0;
  }
  else $rounds=1;
  //move trunk
  $qry="SELECT `order` FROM cw_channel WHERE id='$id'";
  $res=$db->Execute($qry);
  if($res!==false){
    $arr = $res->FetchRow();
    $idFrom=$id;
    $orderFrom=$arr["order"];
    for($i=0;$i<$rounds;$i++){
      //calculate the new positions
      if ($direction==1) {
        $qry="SELECT `order`,id FROM cw_channel WHERE `order` < $orderFrom and trunk=1 ORDER BY `order` DESC";
        $res=$db->Execute($qry);
        $arr = $res->FetchRow();
        $idTo=$arr["id"];
        $orderTo=$arr["order"];
      } else if ($direction==2) {
        $qry="SELECT `order`,id FROM cw_channel WHERE `order` > $orderFrom and trunk=1 ORDER BY `order`";
        $res=$db->Execute($qry);
        $arr = $res->FetchRow();
        $idTo=$arr["id"];
        $orderTo=$arr["order"];
      }
      if (($idFrom=="") or ($idTo=="")) return 0;
      //update trunks table
      $res=$db->Execute("UPDATE cw_channel SET `order`=$orderTo WHERE id=$idFrom");
      $res=$db->Execute("UPDATE cw_channel SET `order`=$orderFrom WHERE id=$idTo");
      //refresh orderFrom
      $orderFrom=$orderTo;
    }
  }
  //refresh the general global variable
  generateGlobalVar();
  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  //if($status=="ON") sendCommand("extensions reload");
  if($status=="ON") sendCommand("dialplan reload");
  //return
  return 1;
}

// ---- putChannel(Channel:Channel):int ---------------------------------------
function putChannel($channel) {
  global $db,$asteriskAppDelimeter;
  $technology=$channel["technology"];
  $resource=$channel["resource"];
  $msns=$channel["msns"];
  $did=$channel["did"];
  $key=$channel["key"];
  $rule=$channel["rule"];
  $speedDial=$channel["speeddial"];
  $order=$channel["order"];
  $trunk=$channel["trunk"];
  $enable_cid=$channel["enable_cid"];
  $cid_list=$channel["cid_list"];
  $cid_number=$channel["cid_number"];
  $cid_name=$channel["cid_name"];
  $cid_appexten=$channel["cid_appexten"];
  $force_cid=$channel["force_cid"];
  $clir=$channel["clir"];
  $location=$channel["location"];
  $area_code=$channel["area_code"];
  $prefix_policy=$channel["prefix_policy"];
  $prefix_format=$channel["prefix_format"];
  if ($key=="") $key="0";
  if ($did=="") $did="0";
  if ($order=="") $order="0";
  if ($trunk=="") $trunk="0";
  if ($rule=="")  $rule="0";
  if ($enable_cid=="") $enable_cid="0";
  if ($cid_appexten=="") $cid_appexten="0";
  if ($force_cid=="") $force_cid="0";
  if ($clir=="")  $clir="0";
  if ($prefix_policy=="")  $prefix_policy="0";
  if ($prefix_format=="")  $prefix_format="0";
  //if already exists, retrieve information
  if(isset($channel["id"]) and $channel["id"]!="") {
    $oldChannel=getChannel($channel["id"],0);
    $oldSpeedDial=$oldChannel[0]["speeddial"];
  }
  //check speeddial (must be unique)
  if(!((isset($channel["id"]))and($channel["id"]!="")and($speedDial==$oldSpeedDial))) {
    $allext=getExtension(0);
    if(is_array($allext)){
      foreach($allext as $e)
        if($speedDial==$e["extension"]) return 0;
    }
  }
  //if already exists, delete it
  if((isset($channel["id"]))and($channel["id"]!="")){
    delChannel($channel["id"],false);
    $entity["id"]=$channel["id"];
  }
  //get the technology name and compute the Channel name
  $qry="SELECT * FROM cw_technology WHERE id=".$technology;
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $techName=$arr["name"];
  $channelName=$techName."/".$resource;
  //implode the phone number listening in
  $msnsString=msns2string($msns);
  $cid_listString=msns2string($cid_list);
  //create a new entity
  $entity["type"]=CHANNEL;
  $entity_id=putEntity($entity);
  if(is_object($entity_id) or $entity_id==0) return 0;
  //check speeddial
  if($trunk==1){
    if($speedDial==""){
      $speedDial=getNextSpeedDial();
      if($speedDial==0) return 0;
    }else $speedDial=$speedDial."*";
  }else $speedDial="";
  //check channel's order
  if($order==0 or $order=="") {
    $qry="SELECT MAX(`order`) as last FROM cw_channel";
    $res=$db->Execute($qry);
    $orderArr=$res->FetchRow();
    $order=$orderArr["last"]+1;
  }
  //insert the Channel in the database
  $qry="INSERT INTO cw_channel (`id`,`name`,`technology`,`key`,`msns`,`did`,`rule`,`speeddial`,`order`,`trunk`,`enable_cid`,";
  $qry.="`cid_list`,`cid_number`,`cid_name`,`cid_appexten`,`force_cid`,`clir`,`location`,`area_code`,`prefix_policy`,`prefix_format`) ";
  $qry.="VALUES ($entity_id,'".string2sql($channelName)."',$technology,$key,'".string2sql($msnsString)."',$did,$rule,";
  $qry.="'".string2sql($speedDial)."',$order,$trunk,$enable_cid,'".string2sql($cid_listString)."','".string2sql($cid_number)."',";
  $qry.="'".string2sql($cid_name)."',$cid_appexten,$force_cid,$clir,'".string2sql($location)."','".string2sql($area_code)."','".string2sql($prefix_policy)."','".string2sql($prefix_format)."')";
  $res=$db->Execute($qry);
  if($trunk==1){
    //write the INCOMING context
    $context="trunk_".$entity_id;
    putAstConfig("extensions.conf", $context, "", "", "$channelName Channel- Incoming context",1);
    // DID ENABLED: extract the base number (i.e. if we have a range like 123450-123499, the base number is 1234)
    if ($did!=0) {
      $baseNumber=extractBaseNumber($msnsString);
      putAstConfig("extensions.conf",$context,"exten","$baseNumber,1,NoOp(Incoming-$baseNumber:$context CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
      putAstConfig("extensions.conf",$context,"exten","$baseNumber,n,AGI(incoming.php".$asteriskAppDelimeter."answered=\${EXTEN}&channel=$entity_id&rule=$rule&uniqueid=\${UNIQUEID})");
      putAstConfig("extensions.conf",$context,"exten","_$baseNumber.,1,NoOp(Incoming-$baseNumber:$context CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
      putAstConfig("extensions.conf",$context,"exten","_$baseNumber.,n,AGI(incoming.php".$asteriskAppDelimeter."answered=\${EXTEN}&channel=$entity_id&rule=$rule&uniqueid=\${UNIQUEID})");
    } else {
    // DID DISABLED - list all the available numbers
      $numbers=explode("|",$msnsString);
      if (is_array($numbers))
      foreach($numbers as $n) {
        if ($n!="") {
          putAstConfig("extensions.conf",$context,"exten","$n,1,NoOp(Incoming-$n:$context CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
          putAstConfig("extensions.conf",$context,"exten","$n,n,AGI(incoming.php".$asteriskAppDelimeter."answered=$n&channel=$entity_id&rule=$rule&uniqueid=\${UNIQUEID})");
        }
      }
    }
    putAstConfig("extensions.conf",$context,"exten","s,1,NoOp(Incoming-s. CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
    putAstConfig("extensions.conf",$context,"exten","s,n,AGI(incoming.php".$asteriskAppDelimeter."answered=\${EXTEN}&channel=$entity_id&rule=$rule&uniqueid=\${UNIQUEID})");
    //if ($did!=0) putAstConfig("extensions.conf",$context,"exten","s,1,AGI(dial.php,act=did&rule=".$did."&extension=)");
    putAstConfig("extensions.conf",$context,"exten","t,1,Hangup");
    //write the OUTGOING context
    $context="trunk_out_".$entity_id;
    if ($channel["key"]!="") $metaGroup=getMetaGroupFromEntity($channel["key"]);
    else $metaGroup=GROUP_OF_RESOURCE;
    putAstConfig("extensions.conf", $context, "", "", "$channelName Channel- Outgoing context",1);
    putAstConfig("extensions.conf",$context,"exten","_".$speedDial.".,1,AGI(dial.php,entity=".$entity_id."&group=".GROUP_OF_RESOURCE."&extension=".$speedDial.")");
    if ($metaGroup==GROUP_OF_OFFICE)
      putAstConfig("extensions.conf",$context,"exten",$speedDial.",1,AGI(dial.php,entity=".$key."&group=".$metaGroup."&extension=".$speedDial.")");
    addEntityToGroup($entity_id,array());
    //insert the speeddial in cw_extension
    $entry=array();
    $entry["id"]=0;
    $entry["entity_id"]=$entity_id;
    $entry["extension"]=$speedDial;
    $res=putExtension($entry);
  }
  //regenerate globals var
  generateGlobalVar();
  //Prune client Cid settings if they don't link a valid channel number
  if(isset($channel["id"]) and $channel["id"]!="") {
  	pruneCidClient($channel["id"],$cid_list);
  }
  //if asterisk is ON, reload extensions
  if(($key=="")or($key=="0")){
    $status=pbxSwitch("status");
    //if($status=="ON") sendCommand("extensions reload");
    if($status=="ON") sendCommand("dialplan reload");
  }
  //return
  return $entity_id;
}

// ---- delChannel(id:int):int ------------------------------------------------
function delChannel($id, $reload=true) {
  global $db;
  $channel=getChannel($id,0);
  $extension=$channel[0]["speeddial"];
  //delete from cw_channel
  $qry="DELETE FROM `cw_channel` WHERE `id`= $id";
  $res=$db->Execute($qry);
  //delete the Channel from the ast_config and extensions_table
  $context="trunk_".$id;
  $res=delAstConfig("extensions.conf",$context,"%");
  if ($id>0) $db->Execute("DELETE FROM extensions_table WHERE context='".string2sql($context)."'");
  //delete the channel_out from the ast_config and extensions_table
  $context="trunk_out_".$id;
  $res=delAstConfig("extensions.conf",$context,"%");
  if ($id>0) $db->Execute("DELETE FROM extensions_table WHERE context='".string2sql($context)."'");
  //remove the resource from the asterisk tables
  removeEntityFromGroup($id);
  //delete all channel's cid settings in cw_cid
  if($reload===true){
    delCid($id,0);
  }
  //regenerate globals var
  generateGlobalVar();
  //delete from cw_extension
  $ret=delExtensionByExtension($extension);
  //if asterisk is ON, reload extensions
  if ($reload===true){
    $status=pbxSwitch("status");
    //if($status=="ON") sendCommand("extensions reload");
    if($status=="ON") sendCommand("dialplan reload");
  }
  // return value
  return 1;
}


//******************************************************************************
//******************               PHONENUMBERS                  ***************
//******************************************************************************

// ---- getPhoneNumber(id:int):phonenumber[] -----------------------------------
function getPhoneNumber($id) {
  global $db;
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_number $condition ORDER BY name";
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) {
    //extract the numbers if many classes are defined
    if($v["number"]!="") $v["number"]=explode("|",$v["number"]);
    else $v["number"]=array();
    //retrieve the client groups
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=".$v["id"];
    $resg=$db->Execute($qry);
    while ($arrg=$resg->FetchRow()) {
      $v["groups"][]=$arrg["group_id"];
    }
    $ret[]=$v;
  }
  // return
  return $ret;
}

// ---- putPhoneNumber(number:PhoneNumber):int ---------------------------------
function putPhoneNumber($number) {
  global $db;

  $ret=1;
  $entity=array();

  //if the phonenumber already exists, delete it
  if (isset($number["id"]) and $number["id"]!="") {
    delPhoneNumber($number["id"]);
    $entity["id"]=$number["id"];
  }

  //create a new entity
  $entity["type"]=PHONENUMBER;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;

  //insert the phonenumber credentials
  $name=$number['name'];
  $emergency=$number['emergency'];
  $pn=implode("|",$number["number"]);
  $qry="INSERT INTO cw_number (id,name,number) ";
  $qry.="VALUES ($entity_id,'".string2sql($name)."','".string2sql($pn)."')";
  $res=$db->Execute($qry);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;

  //insert the extensions in the extensions_table
  $context=$number["name"];
  foreach ($number["number"] as $n) {
    putAstConfig("extensions.conf", $context, "exten", "$n,1,AGI(dial.php,entity=$entity_id&group=".GROUP_OF_PHONENUMBER."&extension=$n)");
  }

  //insert the extensions
  $res=addEntityToGroup($entity_id, $number["groups"]);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;

  //return value
  if ($ret==1) $ret=$entity_id; else $ret=0;

  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");

  return $ret;
}

// ---- delPhoneNumber(id:int):int ---------------------------------------------

function delPhoneNumber($entity_id) {
  global $db;

  //retrieve the tech type from the client
  $qry="SELECT * FROM cw_number WHERE id=$entity_id";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $name=$arr["name"];

  if ($name!="") {
    //remove the entry from the ast_config
    $res=delAstConfig("extensions.conf",$name);

    //delete the extensions from the extensions_table
    $db->Execute("DELETE FROM extensions_table WHERE context='".string2sql($name)."'");

    //remove all the client inclusions
    if (trim($name)!="") $res=delAstConfig("extensions.conf","%","include",$name);
  }

  //remove the client from the asterisk tables and groups
  $ret=removeEntityFromGroup($entity_id);

  //remove the entity
  $ret=delEntity($entity_id);


  //cancello il client
  $qry="DELETE FROM cw_number WHERE id=".$entity_id;
  $db->Execute($qry);

  return $ret;
}

//******************************************************************************
//******************               GROUP                    ********************
//******************************************************************************

// ---- getGroup(id:int,type:int):Group[] --------------------------------------
/**
 * Get Group by id and type.
 *
 * Get all groups of a given type if id is not defined
 * Get all groups of all types if id and type is not defined
 *
 * @param integer groupID. If none specified, then all groups of the given type  are returned
 * @param integer Type of group (extensions, numbers etc.). If none specified, then all types depending on id are returned
 * @return array group[] using the database record structure
 * @see constants.php
 */

function getGroup($id,$type=0) {
  global $db;
  $count=0;
  if ($type!=0) $condition="WHERE type=$type ";
  if ($id!="") $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_group $condition ORDER BY type, name";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $group_id=$arr["id"];

    //retrieve the entity array
    unset($entity);
    $qry="SELECT entity_id FROM cw_group_entity WHERE group_id=$group_id";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) $entity[]=$arr2["entity_id"];
    $arr["entity"]=$entity;

    $ret[]=$arr;
  }
  return $ret;
}
function resolveExstensionGroup($id){
  global $db;
  if($id==0) return array();
  $ret=array();
  $qry="SELECT * FROM cw_group_entity WHERE group_id='".addslashes($id)."'";
  $res=$db->Execute($qry);
  if($res!==false){
    while($arr=$res->FetchRow()){
      $qry="SELECT name FROM `cw_channel` WHERE `key`='".addslashes($arr["entity_id"])."'";
      $res_c=$db->Execute($qry);
      if(($res_c!==false)&&($c=$res_c->FetchRow())){
        $ret[]=$c["name"];
      }
    }
  }
  return $ret;
}
// ---- putGroup(group:Group):int ----------------------------------------------

function putGroup($group) {
  global $db;

  //go to the alterGroup function
  if (isset($group["id"]) and $group["id"]!="") {
    $group_id=$group["id"];
    //remove the group
    delGroup($group_id);
    $id_field="`id`,";
    $id_value=$group_id.", ";
  }

  $ret=1;
  $name=$group["name"];
  $type=$group["type"];
  $metagroup=$group["metagroup"];
  if ($metagroup==0) $metagroup="0";
  $qry="INSERT INTO cw_group ($id_field `name`,`type`,`metagroup`) ";
  $qry.="VALUES ($id_value '".string2sql($name)."',$type,$metagroup)";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Group creation error, mysql INSERT command failed');
  else if (!isset($group_id)) $group_id=$db->Insert_ID();

  //insert the group into Asterisk
  if (is_array($group["entity"]))
  foreach ($group["entity"] as $e) {
    addEntityToGroup($e,array($group_id));
  }

  //return value
  if ($ret>0) $ret=$group_id;

  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");

  return $ret;
}

// ---- delGroup(id:int):int --------------------------------------------------

function delGroup($id) {
  global $db;

  //retrieve the group name
  $g=getGroup($id);
  $name=$g[0]["name"];

  $qry="DELETE FROM cw_group WHERE id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Group remove error, mysql DELETE command failed');

  $qry="DELETE FROM cw_group_entity WHERE group_id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Group-entity remove error, mysql DELETE command failed');

  //remove the group related entry in the ast_config table
  //if (trim($name)!="") {
  //  $res=delAstConfig("extensions.conf",$name);
  //  $res=delAstConfig("extensions.conf","%","include",$name);
  //}

  return 1;
}

//******************************************************************************
//******************             SANDBOX ACCESS            *********************
//******************************************************************************

// ---- getFileFromSandbox(path:string):base64Binary  --------------------------
function getFileFromSandbox($path){
  global $voiceoneWSDocumentRoot;
  // Encode the file ready to send it off
  $file=$voiceoneWSDocumentRoot."/sandbox".$path;
  $handle = fopen($file,"rb");
  $file_content = fread($handle,filesize($file));
  fclose($handle);
  $encoded = base64_encode($file_content);
  $memoryInUse=memory_get_usage();
  return $encoded;
}

//******************************************************************************
//******************               ISDN CARD               *********************
//******************************************************************************

// MOVED IN ISDN MODULE

//******************************************************************************
//******************               OPTIONS                 *********************
//******************************************************************************

// ---- getOptionByName(name:string,category:string):Option[] -----------------

function getOptionByName($name,$category) {
  global $db;
  $count=0;
  $condition="WHERE name='".string2sql($name)."' and category='".string2sql($category)."' ";
  $qry="SELECT * FROM cw_option $condition ORDER BY category";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) $ret[]=$arr;
  return $ret;
}

// ---- getConfOption(id:int,category:string):Option[] -----------------------------------------------

function getConfOption($id,$category="",$section="") {
  global $db;
  $count=0;
  if ($id!="") $condition="WHERE id=$id ";
  else if ($category!="") {
    $condition="WHERE category='".string2sql($category)."' ";
		if (($section!="")&&($section==SECTION_GENERAL)) $condition.="AND general_section=1 ";
		else if (($section!="")&&($section==SECTION_DEFAULT)) $condition.="AND default_section=1 ";
	}
  $qry="SELECT * FROM cw_option $condition ORDER BY category";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) $ret[]=$arr;
  return $ret;
}

// ---- putOption(tech:int, options:AssociativeArray[]):int --------------------
//use it for the general options
function putOption($tech,$options=null,$section="",$reload=true) {
  global $db;
  //get the tech infos
  $techArr=getTechnology($tech);
  $techName=$techArr[0]["name"];
  $techGeneralContext=$techArr[0]["general_context"];
  //get all the options
  $allOptionsArr=getConfOption(0,$techName,$section);
  //get the custom options
  if (is_array($options))
  foreach ($options as $o) {
    $optionsArr[$o["key"]]=$o["value"];
  }
  //cycle between options and write the new ones
  if (is_array($allOptionsArr)){
	  if (is_array($optionsArr)){
      foreach ($allOptionsArr as $i=>$o) {
        $option_id=$o["id"];
        $value=string2sql($o["value"]);
        if (@array_key_exists($option_id,$optionsArr)) {
          $allOptionsArr[$i]["commented"]="0";
          $allOptionsArr[$i]["value"]=$optionsArr[$option_id];
          $commented="0";
          $value=$optionsArr[$option_id];
        } else {
          $allOptionsArr[$i]["commented"]="1";
          $commented="1";
        }
        $qry="UPDATE cw_option set value='".string2sql($value)."', commented=$commented WHERE id=$option_id";
        $db->Execute($qry);
  		}
		} else { //passed an empty options array
  	  foreach ($allOptionsArr as $i=>$o) {
        $option_id=$o["id"];
        $value=string2sql($o["value"]);
  			$allOptionsArr[$i]["commented"]="1";
        $commented="1";
  			$qry="UPDATE cw_option set value='".string2sql($value)."', commented=$commented WHERE id=$option_id";
        $db->Execute($qry);
      }
		}
  }
  //write the config file
  $ret=putOptionInAstConfig($tech,$allOptionsArr,$reload);
  //return value
  return $ret;
}

// ---- putOptionInAstConfig(tech:int, options:AssociativeArray[]):int ---------
function putOptionInAstConfig($tech,$allOptionsArr,$reload=true) {
  global $db;
  //get the tech infos
  $techArr=getTechnology($tech);
  $techName=$techArr[0]["name"];
  $techGeneralContext=$techArr[0]["general_context"];
  //get all the options
  if(!(is_array($allOptionsArr))) $allOptionsArr=getConfOption(0,$techName);
  //write the config file
  $localnetAlreadyDeleted=false;
  $config_file=$techArr[0]["config_file"];
  if (is_array($allOptionsArr))
  foreach ($allOptionsArr as $i=>$o) {
    if($o["value"]=="") $value=$o["default"]; else $value=$o["value"];
    if($o["general_section"]==1) $context=$techGeneralContext;
    if($o["general_section"]==0 and $tech==MISDN and $o["default_section"]==1) $context="default";
    if($o["general_section"]==0 and $o["default_section"]==0) continue;
    if(($localnetAlreadyDeleted===false)||(trim($o["name"])!="localnet")){
      delAstConfig($config_file, $context, $o["name"], "");
    }
    if(($context==$techGeneralContext)&&(trim($o["name"])=="localnet")) $localnetAlreadyDeleted=true;
    if(($context==$techGeneralContext)&&($o["name"]=="context")) continue; // DON'T WRITE "context = value" in tecnologies general context
    if($o["commented"]==0){
      putAstConfig($config_file, $context, $o["name"], $value, "", 0, "");
    }
    if($tech==ZAP) optimizeAstConfig($tech);
  }
  //if asterisk is ON, reload extensions
  if ($reload===true){
    $status=pbxSwitch("status");
    if($status=="ON") sendCommand("reload");
  }
  //return value
  return 1;
}

// ---- getEntityOption(entity_id:int):int ----
// private

function getEntityOption($entity_id) {
  global $db;
  $ret=Array();

  //retrieve the client specific options
  $qry="SELECT * FROM cw_option_entity  WHERE entity_id=$entity_id";
  $resOpt=$db->Execute($qry);
  while ($arr=$resOpt->FetchRow()) {
    $tmpArr["key"]=$arr["option_id"];
    $tmpArr["value"]=$arr["value"];
    $ret[]=$tmpArr;
  }
  return $ret;
}


// ---- putEntityOption(options:AssociativeArray, tech:int, entity_id:int, otherOptions:stringArray):int ----
// private
function putEntityOption($options,$tech,$entity_id,$otherOptions) {
  global $db,$keyName;

  //get the tech infos
  $techArr=getTechnology($tech);
  $techName=$techArr[0]["name"];
  $techVoip=$techArr[0]["voip"];
  $techRealTime=$techArr[0]["realtime"];
  $techConfigFile=$techArr[0]["config_file"];
  $techGeneralContext=$techArr[0]["general_context"];

  //get the entity type
  $entityArr=getEntity($entity_id);
  $entityType=$entityArr[0]["type"];

  //get all the options
  $allOptionsArr=getConfOption(0,$techName,0);

  //cycle between options
  if(is_array($options)){
    foreach($options as $i=>$o) {
      $option_id=$o["key"];
      $value=string2sql($o["value"]);

      //Retrieve the option name
      $qry="SELECT name,type FROM cw_option WHERE id=$option_id";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $options[$i]["name"]=$arr["name"];
      $options[$i]["type"]=$arr["type"];

      //insert/update the option value in the cw_option_entity table
      $qry="SELECT * FROM cw_option_entity WHERE option_id=$option_id AND entity_id=$entity_id";
      $res=$db->Execute($qry);
      if ($res->RecordCount()==0)
        $qry="INSERT INTO cw_option_entity (option_id,entity_id,value) VALUES ($option_id,$entity_id,'".$value."')";
      else
        $qry="UPDATE cw_option_entity  set value='".$value."' WHERE option_id=$option_id AND entity_id=$entity_id";
      $db->Execute($qry);
    }
  }
  //get the context name
  $context=getEntityName($entity_id);
  if($techRealTime==1){
    //if ($entityType==OFFICE) $options[]=array("name"=>"name", "value"=>$otherOptions["username_local"]);
    $options[]=array("name"=>"name", "value"=>$context);
  }
  //SIP & IAX
  if(($tech==SIP or $tech==IAX2) and $entityType==EXTENSION) {
    $options[]=array("name"=>"secret",    "value"=>$otherOptions["password"]);
    
    if($tech==SIP){
      //$options[]=array("name"=>"username",  "value"=>$context);  // FOR ASTERISK 1.2
      $options[]=array("name"=>"defaultuser",  "value"=>$context); // FOR ASTERISK 1.6
    }
    if($tech==IAX2){
      $options[]=array("name"=>"username",  "value"=>$context);  // FOR ASTERISK 1.2 AND 1.6
    }
    if(!array_key_exists_recursive("callerid",$options)){
      $options[]=array("name"=>"callerid",  "value"=>$otherOptions["name"]." ".$otherOptions["surname"]." <".$otherOptions["extension"].">");
    }
    if(!array_key_exists_recursive("host",$options)){
      $options[]=array("name"=>"host",  "value"=>"dynamic");
    }
    // Fix MWI until we have a flag MWI yes/no in voicemail setup
    if(array_key_exists("voicemail",$otherOptions)){
      $options[]=array("name"=>"mailbox",  "value"=>$otherOptions["voicemail"]["mailbox"]);
    }
    // Fix qualify=yes for iax extensions
    if($tech==IAX2){
      if(!array_key_exists_recursive("qualify",$options))
        $options[]=array("name"=>"qualify",  "value"=>"yes");
    }
  }
  //MISDN
  if($tech==MISDN and $entityType==EXTENSION){
    $options[]=array("name"=>"msns",  "value"=>$otherOptions["extension"]);
    if(!array_key_exists_recursive("callerid",$options)){
      $options[]=array("name"=>"callerid",  "value"=>"\"".$otherOptions["name"]." ".$otherOptions["surname"]."\" <".$otherOptions["extension"].">");
    }
  }
  //ZAP
  if($tech==ZAP){
    $options[]=array("name"=>"signalling",  "value"=>$otherOptions["signalling"]);
    if($entityType==EXTENSION){
      $options[]=array("name"=>"group",  "value"=>"0");
      if(!array_key_exists_recursive("callerid",$options)){
        $options[]=array("name"=>"callerid",  "value"=>"\"".$otherOptions["name"]." ".$otherOptions["surname"]."\" <".$otherOptions["extension"].">");
      }
      if(!array_key_exists_recursive("mailbox",$options)){
        $options[]=array("name"=>"mailbox",  "value"=>$otherOptions["voicemail"]["mailbox"]);
      }
    }else{
      $options[]=array("name"=>"group",  "value"=>$otherOptions["group"]);
    }
  }
  //PROVIDER ACCOUNT
  if($techVoip==1 and $entityType==PROVIDER_ACCOUNT){
    // This needs to have a flag for CallerId
    if (!array_key_exists_recursive("callerid",$options))
      $options[]=array("name"=>"callerid",  "value"=>$otherOptions["p_name"]);
    if (!array_key_exists_recursive("fromuser",$options))
      $options[]=array("name"=>"fromuser",  "value"=>$otherOptions["fromuser"]);
    if (!array_key_exists_recursive("fromdomain",$options))
      $options[]=array("name"=>"fromdomain",  "value"=>$otherOptions["fromdomain"]);
    if (!array_key_exists_recursive("host",$options))
      $options[]=array("name"=>"host",  "value"=>$otherOptions["host"]);
    if (!array_key_exists_recursive("username",$options))
      //$options[]=array("name"=>"username",  "value"=>$otherOptions["username"]);
      $options[]=array("name"=>"defaultuser",  "value"=>$otherOptions["username"]);
    if (!array_key_exists_recursive("secret",$options))
      $options[]=array("name"=>"secret",  "value"=>$otherOptions["password"]);
    if (!array_key_exists_recursive("port",$options))
      $options[]=array("name"=>"port",  "value"=>$otherOptions["port"]);
    //if (!array_key_exists_recursive("insecure",$options))
    //  $options[]=array("name"=>"insecure",  "value"=>"very");
  }
  //OFFICE
  if($entityType==OFFICE){
    //$options["name"]=$otherOptions["username_local"];
    if (!array_key_exists_recursive("callerid",$options))
      $options[]=array("name"=>"callerid",  "value"=>$otherOptions["name"]);
    if (!array_key_exists_recursive("fromuser",$options))
      $options[]=array("name"=>"fromuser",  "value"=>$otherOptions["username"]);
    if (!array_key_exists_recursive("fromdomain",$options))
      $options[]=array("name"=>"fromdomain",  "value"=>$otherOptions["host"]);
    if (!array_key_exists_recursive("host",$options))
      $options[]=array("name"=>"host",  "value"=>$otherOptions["host"]);
    if (!array_key_exists_recursive("username",$options))
      $options[]=array("name"=>"username",  "value"=>$otherOptions["username"]);
    if (!array_key_exists_recursive("secret",$options))
      $options[]=array("name"=>"secret",  "value"=>$otherOptions["password"]);
    if (!array_key_exists_recursive("port",$options))
      $options[]=array("name"=>"port",  "value"=>$otherOptions["port"]);
    //if (!array_key_exists_recursive("auth",$options))
    if ($otherOptions["inkey"]!="") {
      $options[]=array("name"=>"auth",  "value"=>"rsa");
      $options[]=array("name"=>"inkeys",  "value"=>$otherOptions["name"]);
      $options[]=array("name"=>"outkey",  "value"=>$keyName);
    } else $options[]=array("name"=>"auth",  "value"=>"plaintext");
  }
  //case REALTIME
  if ($techArr[0]["realtime"]==1) {
    $table=$techArr[0]["table"];
    foreach ($options as $i=>$o) {
      if(strtoupper($o["value"])=="NULL"){
        $field_list.="`".$o["name"]."`,";
        $value_list.="NULL,";
      }else{
        $field_list.="`".$o["name"]."`,";
        $value_list.="'".string2sql($o["value"])."',";
      }
    }
    $field_list=substr($field_list,0,-1);
    $value_list=substr($value_list,0,-1);
    $qry="INSERT INTO $table ($field_list) VALUES ($value_list)";
    $db->Execute($qry);
  }
  //case NOT REALTIME
  else{
    $zsection="";
    if($tech==ZAP){ $zsection=$context; $context=$techGeneralContext;}
    foreach ($options as $i=>$o) {
      putAstConfig($techConfigFile, $context, $o["name"], $o["value"], '', '0' , $zsection);
    }
    if($tech==ZAP) optimizeAstConfig($tech);
  }
  return true;
}


// ---- delEntityOption(id:int):int --------------------------------------------------
// private

function delEntityOption($entity_id, $tech) {
  global $db;

  //get the tech infos
  $techArr      = getTechnology($tech);
  $techName     = $techArr[0]["name"];
  $techRealTime = $techArr[0]["realtime"];
  $techTable    = $techArr[0]["table"];
  $techFile     = $techArr[0]["config_file"];
  $techGeneralContext=$techArr[0]["general_context"];

  //remove options from the table
  $qry="DELETE FROM cw_option_entity WHERE entity_id=$entity_id";
  $res=$db->Execute($qry);

  //get the context name
  $context=getEntityName($entity_id);
  if ($context=="") return 0;

  //delete from asterisk table REALTIME/STATIC
  if ($techRealTime==1) $db->Execute("DELETE FROM $techTable WHERE name='$context'");
  else{
    $zsection="";
    if($tech==ZAP){
      $zsection=$context;
      $context=$techGeneralContext;
    }
    delAstConfig($techFile, $context, '', '', $zsection);
    if($tech==ZAP) optimizeAstConfig($tech);
  }
  //return
  return 1;
}

//******************************************************************************
//******************               APPLICATIONS             ********************
//******************************************************************************
/**
 * Get application by id or get all applications if id is not defined
 *
 * @param integer applicationID. If none specified, then all applications are returned
 * @return array Application[] using the database record structure  and add groups
 * @see putApplication
 * @see delApplication
 * @see getApplicationByName
 * @todo fix group export to be xml compliant (do not use numeric tags)
 */

// ---- getApplication(id:int):Application[] -----------------------------------

function getApplication($id=0) {
  global $db;
  $count=0;
  if ($id!="") $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_application $condition ORDER BY name";
  $res=$db->Execute($qry);

  while ($arr=$res->FetchRow()) {
    //retrieve the client groups
    $arr["groups"]=Array();
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=".$arr["id"];
    $resGrp=$db->Execute($qry);
    while ($arrGrp=$resGrp->FetchRow()) $arr["groups"][]=$arrGrp["group_id"];

    $ret[]=$arr;
  }

  return $ret;
}

/**
 * Get application by name
 *
 * @param string $name
 * @return array Application[] using the database record structure  and add groups
 * @see putApplication
 * @see delApplication
 * @see getApplication
 * @todo fix group export to be xml compliant (do not use numeric tags)
 */
function getApplicationByName($name) {
  global $db;
  $count=0;
  $qry="SELECT * FROM cw_application WHERE name='".string2sql($name)."' ORDER BY label";
  $res=$db->Execute($qry);

  while ($arr=$res->FetchRow()) {
    //retrieve the client groups
    $arr["groups"]=Array();
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=".$arr["id"];
    $resGrp=$db->Execute($qry);
    while ($arrGrp=$resGrp->FetchRow()) $arr["groups"][]=$arrGrp["group_id"];
    $ret[]=$arr;
  }

  return $ret;
}


// ---- putApplication(app:Application, $keepId:Boolean):int -----------------------------------

/**
 * Add an application to the system
 *
 * If the parameter array contains a value for ['id'] than putApplication tries to update the application and keeps the id
 * @param array $app app:Application
 * @param boolean $keepId default: true=keep Id regardless of the name; false=keep Id only if name matches
 * @return integer $entityId or 0 if failure
 * @see getApplication
 * @see delApplication
 * @todo FIX BUG in groups / apps / metagroup handling.  disabled group for now.
 */
function putApplication($app, $keepId=true) {
  global $db;

  $entity=array();
  $ret=1;

  //if already exists, retrieve information
  if (isset($app["id"]) and $app["id"]!="") {
    $oldApp=getApplication($app["id"]);
    $oldSpeedDial=$oldApp[0]["extension"];
    $agent_id=getAgentFromEntity($app["id"]);
    if($agent_id!=0){
      $agentArr=getAgent($agent_id);
      if((is_array($agentArr))&&(is_array($agentArr[0]))){
      	$agent=$agentArr[0];
      }
    }
  }
  //check speeddial (must be unique)
  if(!((isset($app["id"]))and($app["id"]!="")and($app["extension"]==$oldSpeedDial))) {
    $allext=getExtension(0);
    if(is_array($allext)){
      foreach($allext as $e)
        if($app["extension"]==$e["extension"]) return 0;
    }
  }

  // Request for Update?
  // By name or by id?
  // Update via edit would be by id
  // Update via plugin would be by name
  // Otherwise we would risk to override a complete different entity via a plugin
  // So $keepId should be false if called via putPlugin.

  if ((isset($app["id"])) and ($app["id"] != "") and ($keepId === true)) {
    // id is set for new app and must be used
      $entity["id"] = $app["id"];
      delApplication($entity["id"], false);
  } elseif ((isset($app["name"])) and ($app["name"] != "") and ($keepId === false)) {
    // name is set for new app and must be used
    $curApp = getApplicationByName($app["name"]);
    if ($curApp[0]['name'] == $app['name']) {
      // name matches, so update is ok
      $entity["id"] = $curApp[0]['id'];
      delApplication($entity["id"], false);
    }
  }

  //create a new entity
  $entity["type"]=APPLICATION;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;

  //insert the application credentials
  $label=$app["label"];
  if ($app["name"]=="") $name=preg_replace("/[^0-9a-z]/i",'',$app["label"])."_".$entity_id; else $name=$app["name"];
  $description=$app["description"];
  $extension=$app["extension"];
  $code=$app["code"];
  $macro=$app["macro"];
  $parameter=$app["parameter"];
  $groups=$app["groups"];
  $qry="INSERT INTO cw_application (id,name,label,description,extension,code,macro,parameter,groups) ";
  $qry.="VALUES ($entity_id,'".string2sql($name)."','".string2sql($label)."','".string2sql($description)."',";
  $qry.="'".string2sql($extension)."','".string2sql($code)."','".$macro."','".string2sql($parameter)."','".string2sql($groups)."')";
  $r=$db->Execute($qry);
  if ($r===false) $ret=$ret and 0; else $ret=$ret and 1;

  $context="app-$name";
  putAstConfig("extensions.conf", $context, "exten", "$extension,1,AGI(dial.php,entity=$entity_id&group=".GROUP_OF_APPLICATION."&extension=$extension)");

  //insert the application to groups
  $res=addEntityToGroup($entity_id, $groups);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  //insert the speeddial in cw_extension
  if($ret==1) {
    $entry=array();
    $entry["id"]=0;
    $entry["entity_id"]=$entity_id;
    $entry["extension"]=$extension;
    $res=putExtension($entry);
  }
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  //get agent linked to application
  if(is_array($agent)){
  	$agent["extension"]=$extension;
    putAgent($agent);
  }
  //return value
  if ($ret==1) $ret=$entity_id; else $ret=0;
  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");
  return $ret;
}

// ---- delApplication(id:int):int --------------------------------------------------
/**
 * Remove an application by id from the system
 *
 * If the parameter array contains a value for ['id'] than putApplication tries to update the application and keeps the id
 * @param integer $appId id of application
 * @param boolean $reload flag to indicate wether asterisk should reload the configuration
 * @return integer 1
 * @see getApplication
 * @see delApplication
 */

function delApplication($entity_id,$reload=true) {
  global $db;

  //get the entity type
  //$entityArray=getEntity($entity_id);
  //$entity=$entityArray[0];

  //get the application name
  $application=getApplication($entity_id);
  $name="app-".$application[0]["name"];
  $extension=$application[0]["extension"];

  //remove the entry from the ast_config
  $res=delAstConfig("extensions.conf",$name);

  //delete the extensions from the extensions_table
  $db->Execute("DELETE FROM extensions_table WHERE context='$name'");

  //remove all the client inclusions
  if (trim($name)!="") $res=delAstConfig("extensions.conf","%","include",$name);

  //remove the resource from the asterisk tables
  $ret=removeEntityFromGroup($entity_id);

  //remove the entry from the resource table
  $qry="DELETE FROM cw_application WHERE id=".$entity_id;
  $db->Execute($qry);

  //remove from cw_extension
  $ret=delExtensionByExtension($extension);

  //remove the entity
  $ret=delEntity($entity_id);

  //delete agent
  $agent_id=getAgentFromEntity($entity_id);
  if($agent_id!=0){
  	delAgent($agent_id);
  }
  //reload the extensions
  if ($reload===true) {
    //if asterisk is ON
    $status=pbxSwitch("status");
    if($status=="ON"){
      //$qry="DELETE FROM ast_config WHERE filename='extensions.conf' AND category='app-$name'";
      //$db->Execute($qry);

      sendCommand("reload");
      //createAstConfig("extensions.conf");
    }
  }

  return 1;
}


//******************************************************************************
//******************               IVR                      ********************
//******************************************************************************


// ---- getIvr(id:int):Ivr[] ---------------------------------------------------
/**
 * Get IVR by id or get all IVRs if id is not defined
 *
 * @param integer ivrID. If none specified, then all IVRs are returned
 * @return array ivr[] using the database record structure
 */
/*
function getIvr($id=0) {
  global $db;
  if ($id!="") $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_ivr $condition ORDER BY name";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) $ret[]=$arr;
  return $ret;
}

// ---- putIvr(ivr:Ivr):int ----------------------------------------------------

function putIvr($ivr) {
  global $db;
  $ret=1;

  $node=Array("name"=>$ivr["name"],"parent"=>0,"order"=>0);
  $rootNode=putIvrNode($node);

  $name=$ivr["name"];
  $qry = "INSERT INTO cw_ivr (`name`,`rootnode`) VALUES ('".string2sql($name)."',$rootNode)";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Ivr creation error, mysql INSERT command failed');
  $ivr_id=$db->Insert_ID();

  return $ivr_id;
}

// ---- delIvr(id:int):int --------------------------------------------------

function delIvr($id) {
  global $db;

  $qry="DELETE FROM cw_ivr WHERE id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Ivr remove error, mysql DELETE command failed');

  return 1;
}



// ---- getIvrNode(id:int, ivr:int):IvrNode[] -----------------------------------------------

function getIvrNode($id=0,$ivr=0,$parent=0) {
  global $db;

  $count=0;
  $ret=array();

  if ($id==0 AND $ivr==0 AND $parent==0) {
    $qry="SELECT * FROM cw_ivr_node";

  } else if ($ivr!=0) {
    $ivrArr=getIvr($ivr);
    $rootNode=$ivrArr[0]["rootnode"];

    $qry="SELECT * FROM cw_ivr_node WHERE id=$rootNode";
    $res=$db->Execute($qry);
    $arr=$res->FetchRow();
    $ret[]=$arr;

    $qry="SELECT * FROM cw_ivr_node WHERE parent=$rootNode ORDER BY `order`";

  } else if ($parent!=0) {
    $qry="SELECT * FROM cw_ivr_node WHERE parent=$parent ORDER BY `order`";

  } else if ($id!=0) {
    $qry="SELECT * FROM cw_ivr_node WHERE id=$id";
  }  else {return ;}

  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $node_id=$arr["id"];
    $ret[]=$arr;
    $ret=array_merge($ret,getIvrNode(0,0,$node_id));
  }
  return $ret;
}

// ---- putIvrNode(node:IvrNode):int ----------------------------------------------

function putIvrNode($node) {
  global $db;

  extract($node);

  //delete old node
  if ($id!="" and $id!="0") {
    //retrieve the current (old) order number
    $qry="SELECT `order`,`parent` FROM cw_ivr_node WHERE id=$id";
    $res=$db->Execute($qry);
    $arr = $res->FetchRow();
    $oldPos=$arr["order"];
    $parent=$arr["parent"];

    delIvrNode($id);
  } else $id="null";

  //if no order is assigned, append as the last node
  if ($order=="0" or $order=="") {
    if ($parent=="") $parent=0;
    $res=$db->Execute("SELECT COUNT(*) as howmuch FROM cw_ivr_node WHERE parent=$parent");
    $arr=$res->FetchRow();
    $order=$arr["howmuch"]+1;
  }

  //check for swap node
  $qry="SELECT * FROM cw_ivr_node WHERE `order`=$order AND `parent`=$parent";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    $arr = $res->FetchRow();
    $nodeToSwap=$arr["id"];
    //if no order is assigned, append as the last node
    if ($oldPos=="") {
      if ($parent=="") $parent=0;
      $res=$db->Execute("SELECT COUNT(*) as howmuch FROM cw_ivr_node WHERE parent=$parent");
      $arr=$res->FetchRow();
      $oldPos=$arr["howmuch"]+1;
    }
    $qry="UPDATE cw_ivr_node SET `order`=$oldPos WHERE id=$nodeToSwap";
    $db->Execute($qry);
  }

  //insert the new node
  $qry="INSERT INTO cw_ivr_node (`id`,`name`,`parent`,`order`, `key`, `audio`, `macro`, `parameter`, `timesegment`,  `taudio`, `trepeat`, `iaudio`, `irepeat`) ";
  $qry.="VALUES ($id,'".string2sql($name)."','".$parent."','".$order."','".string2sql($key)."','".string2sql($audio)."','".$macro."',";
  $qry.="'".string2sql($parameter)."','".$timesegment."','".string2sql($taudio)."','".$trepeat."','".string2sql($iaudio)."','".$irepeat."')";
  $res=$db->Execute($qry);


  if ($res===false) return new soap_fault('Server','','IVR creation error, mysql INSERT command failed');
  else $node_id=$db->Insert_ID();

  if ($id!="null") $ret=$id; else $ret=$node_id;
  return $ret;
}

// ---- delIvrNode(id:int):int --------------------------------------------------

function delIvrNode($id) {
  global $db;

  $qry="DELETE FROM cw_ivr_node WHERE id=$id";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Ivr remove error, mysql DELETE command failed');
  return 1;
}

// ---- swapIvrNode(id:int, newpos:int):int --------------------------------------------------
function swapIvrNode($id,$parent,$newPos,$oldPos) {
  global $db;

  if ($newPos=="") return;
  if ($oldPos==newPos) return;

  //retrieve the current (old) order number
  $qry="SELECT * FROM cw_ivr_node WHERE `order`=$newPos AND `parent`=$parent";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    $arr = $res->FetchRow();
    $nodeToSwap=$arr["id"];
    $qry="UPDATE cw_ivr_node SET `order`=$oldPos WHERE id=$nodeToSwap";
    $db->Execute($qry);
  }
  return;
}


// ---- moveIvrNode(id:int, direction: int):int --------------------------------------------------
// Direction=1 : up
// Direction=2: down

function moveIvrNode($id,$direction=1) {
  global $db;

  //retrieve the current order number
  $qry="SELECT `order`,`parent` FROM cw_ivr_node WHERE id=$id";
  $res=$db->Execute($qry);
  $arr = $res->FetchRow();
  $order=$arr["order"];
  $parent=$arr["parent"];
  if ($parent=="") $parent=0;


  if ($direction==1 and $order>1) {
    $res=$db->Execute("UPDATE cw_ivr_node SET `order`='$order' WHERE `parent`=$parent AND `order`=".($order-1));
    if ($db->Affected_Rows()>0) $res=$db->Execute("UPDATE cw_ivr_node SET `order`=".($order-1)." WHERE id=$id");
  } else if ($direction==2) {
    $res=$db->Execute("UPDATE cw_ivr_node SET `order`=$order WHERE `parent`=$parent AND `order`=".($order+1));
    if ($db->Affected_Rows()>0) $res=$db->Execute("UPDATE cw_ivr_node SET `order`=".($order+1)." WHERE id=$id");
  }
  if ($res===false) return new soap_fault('Server','','Ivr move error, mysql UPDATE command failed');
  else return 1;

}


// ---- compileIvr($ivr_id:int):int --------------------------------------------

function compileIvr($ivr_id) {
  global $db,$ivrPrefix,$ivrScriptConversionPath;

  //clear the old ivr
  clearIvr($ivr_id);

  //$ret=exec("$ivrScriptConversionPath &",$out);

  //build the new ivr
  _compileIvr($ivr_id);

  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");

  return 1;
}

// ---- clearIvr():int --------------------------------------------------
//private

function clearIvr($ivr_id) {
  global $db,$ivrPrefix;

  $qry="DELETE FROM extensions_table WHERE context like '".string2sql($ivrPrefix.$ivr_id)."%'";
  $res=$db->Execute($qry);

  $res=delAstConfig("extensions.conf",$ivrPrefix.$ivr_id."%");

  return 1;
}

// ----    _compileIvr():int     -----------------------------------------------
//private

function _compileIvr($ivr_id,$parent=0) {
  global $db,$ivrPrefix,$ivrStartingDir;

  //retrieve the parent node name (calculate the context)
  $qry="SELECT * FROM cw_ivr_node WHERE `id`=$parent";
  $resp=$db->Execute($qry);
  $arrp=$resp->FetchRow();
  $context=$ivrPrefix.$ivr_id.$arrp["name"];

  //set the query for cycling through the children nodes
  if ($parent==0) {
    $i=getIvr($ivr_id);
    $rootNode=$i[0]["rootnode"];
    $qry="SELECT * FROM cw_ivr_node WHERE `id`=$rootNode";
  } else
    $qry="SELECT * FROM cw_ivr_node WHERE `parent`=$parent ORDER BY `order`";

  $playBeep=false;
  $priority=1;
  $res=$db->Execute($qry);
  while ($arr = $res->FetchRow()) {
    $nodeId=$arr["id"];
    $nodeName=$arr["name"];
    $nodeParent=$arr["parent"];
    $nodeAudio=str_replace(" ","",substr($arr["audio"],0,strpos($arr["audio"],".")));
    $nodeIAudio=str_replace(" ","",substr($arr["iaudio"],0,strpos($arr["iaudio"],".")));
    $nodeTAudio=str_replace(" ","",substr($arr["taudio"],0,strpos($arr["taudio"],".")));
    $nodeContext=$ivrPrefix.$ivr_id.$nodeName;
    $nodeKey=$arr["key"];
    $nodeMacro=$arr["macro"];
    $nodeIRepeat=$arr["irepeat"];
    $nodeTRepeat=$arr["trepeat"];
    $nodeParameter=$arr["parameter"];

    //branch or leaf?
    $qry="SELECT * FROM cw_ivr_node WHERE `parent`=$nodeId";
    $resp=$db->Execute($qry);
    if ($resp->RecordCount()>0) $nodeFolder=true;
    else $nodeFolder=false;

    //play the audio files
    if ($nodeAudio!="")
      putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",Background(".$ivrStartingDir.$nodeAudio.")");

    //insert the extension KEY for jumping inside the branches
    if ($nodeFolder===true) {
      if ($nodeKey=="")
        putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",Goto($nodeContext".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
      else
        putAstConfig("extensions.conf", $context, "exten", "$nodeKey,1,Goto($nodeContext".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
    }

    //take action
    if ($nodeMacro!=0 and $nodeFolder===false) {
      $macroArr=prepareMacro($nodeMacro,$nodeParameter);
      $macroName=$macroArr[0];
      $macroParam=substr($macroArr[1],1);
      $macroParam=$macroArr[1];
      if ($nodeKey=="")
        putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",Macro(".$macroName.$macroParam.")");
      else
        putAstConfig("extensions.conf", $context, "exten", "$nodeKey,1,Macro(".$macroName.$macroParam.")");
    }

    //insert the INVALID and TIMEOUT extensions
    if ($nodeFolder===true) {
      $ipriority=1;
      $tpriority=1;
      if ($nodeIAudio!="")
        putAstConfig("extensions.conf", $nodeContext, "exten", "i,".($ipriority++).",Background(".$ivrStartingDir.$nodeIAudio.")");
      if ($nodeIRepeat==1)
        putAstConfig("extensions.conf", $nodeContext, "exten", "i,".($ipriority++).",Goto($nodeContext".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
      if ($nodeTAudio!="")
        putAstConfig("extensions.conf", $nodeContext, "exten", "t,".($tpriority++).",Background(".$ivrStartingDir.$nodeTAudio.")");
      if ($nodeTRepeat==1)
        putAstConfig("extensions.conf", $nodeContext, "exten", "t,".($tpriority++).",Goto($nodeContext".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
    }

    //set the playBeep variable
    if ($nodeKey!="") $playBeep=true;

    //recursively create the tree
    if ($nodeFolder===true) _compileIvr($ivr_id,$nodeId);
  }

  //play beep
  if ($playBeep===true)
    putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",Playback(beep)");
    putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",WaitExten()");


  //manage the root node case:
  if ($parent==0) {
    putAstConfig("extensions.conf", $context, "exten", "s,".($priority++).",Hangup");
    putAstConfig("extensions.conf", $context, "exten", "i,1,Goto($context".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
    putAstConfig("extensions.conf", $context, "exten", "t,1,Goto($context".$asteriskAppDelimeter."s".$asteriskAppDelimeter."1)");
  }

  return;
}
*/

//******************************************************************************
//******************               DIRECTORY               ********************
//******************************************************************************

// ---- getDirectory():directory[] -----------------------------------

function getDirectory() {
  global $db;
  $start=false;
  $peers=sendCommand("sip show peers");
  if ($peers!="") {
    $lines=explode("\n",$peers);
    foreach ($lines as $l) {
      $l=trim($l);
      if (strpos($l,"online")!==false) $start=false;
      if ($start) {
        //$infos=split("[ ]+",$l);  // DEPRECATED ON PHP 5.3
        //$userArr=explode("/",$infos[0]);
        $infos=substr($l,0,strpos($l," "));
        $userArr=explode("/",$infos);
        $username=$userArr[1];
        if (strpos($l,"UNKNOWN")!==false) $peerInfo[$username]="UNKNOWN";
        if (strpos($l,"Unmonitored")!==false) $peerInfo[$username]="Unmonitored";
        if (strpos($l,"OK")!==false) $peerInfo[$username]="OK";
      }
      if (strpos($l,"Name")!==false) $start=true;
    }
  }
  $start=false;
  $peers=sendCommand("iax2 show peers");
  if ($peers!="") {
    $lines=explode("\n",$peers);
    foreach ($lines as $l) {
      if (strpos($l,"online")!==false) $start=false;
      if ($start) {
        //$infos=preg_split("[ ]+",$l);  // DEPRECATED ON PHP 5.3
        //$userArr=explode("/",$infos[0]);
        $infos=substr($l,0,strpos($l," "));
        $userArr=explode("/",$infos);
        $username=$userArr[1];
        if (strpos($l,"UNKNOWN")!==false) $peerInfo[$username]="UNKNOWN";
        if (strpos($l,"Unmonitored")!==false) $peerInfo[$username]="Unmonitored";
        if (strpos($l,"OK")!==false) $peerInfo[$username]="OK";
      }
      if (strpos($l,"Name")!==false) $start=true;
    }
  }
  $ext=getClient(0);
  if(is_array($ext)){
    foreach($ext as $v){
      $id=$v["id"];
      $name=$v["name"];
      $surname=$v["surname"];
      $extension=$v["extension"];
      if (isset($peerInfo[$extension])) $status=$peerInfo[$extension];
      else $status="UNKNOWN";
      $ret[]=array(
          "id"        => $id,
          "name"      => $name,
          "username"  => $surname,
          "extension" => $extension,
          "status"    => $status
        );
    }
  }
  //return
  return $ret;
}


//******************************************************************************
//******************               USER                     ********************
//******************************************************************************

// ---- getUser(id:int):user[] -----------------------------------
function getUser($id) {
  global $db,$synDbName;
  //check if table exists (need in setup.php)
  $tablesArr=array();
  $db->SetFetchMode(ADODB_FETCH_NUM);
  $qry="SHOW TABLES";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()) $tablesArr[]=trim($arr[0]);
  $db->SetFetchMode(ADODB_FETCH_ASSOC);
  if(!is_array($tablesArr)) return array();
  else if(!in_array("cw_user",$tablesArr,true)) return array();
  //get users
  $condition='';
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_user $condition";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()){
    $id=$arr["id"];
    $type=$arr["type"];
    $lang=$arr["lang"];
    $entity_id=$arr["entity_id"];
    $active=$arr["active"];
    $fullname='';
    $exten='';
    $technology='';
    if($entity_id!=0){
    	//user is an extension
      $client=getClient($entity_id);
      if((is_array($client))&&(is_array($client[0]))){
        $c=$client[0];
        $fullname=$c["name"]." ".$c["surname"];
        $username=$c["extension"];
        $password=$c["password"];
        $exten=$c["extension"];
        $technology=$c["technology"];
        $entity_id=$entity_id;
      }
      //user is a virtual extension
      else{
      	$virtual=getVirtualExtension($entity_id);
        if((is_array($virtual))&&(is_array($virtual[0]))){
          $v=$virtual[0];
          $fullname=$v["name"]." ".$v["surname"];
          $username=$arr["username"];
          $password=$arr["password"];
          $exten=$v["extension"];
          $entity_id=$entity_id;
        }else continue;
      }
    }
    //user doesn't have extension
    else{
      $username=$arr["username"];
      $password=$arr["password"];
      $entity_id=0;
    }
    $ret[]=array("id"=>$id, "fullname"=>$fullname, "username"=>$username, "password"=>$password, "lang"=>$lang, "entity_id"=>$entity_id, "exten"=>$exten, "technology"=>$technology, "type"=>$type, "active"=>$active);
  }
  return $ret;
}
// ---- getUserIdFromEntity(entity_id:int):int ---------------------------------
function getUserIdFromEntity($entity_id) {
  global $db;
  if(($entity_id=="")||($entity_id==0)) return 0;
  //get user id
  $ret=0;
  $qry="SELECT * FROM cw_user WHERE entity_id=".$entity_id;
  $res=$db->Execute($qry);
  if($res!==false){
    while($arr=$res->FetchRow()){
      $ret=$arr["id"];
    }
  }
  //return
  return $ret;
}
// ---- putUser(user:User):int -----------------------------------
function putUser($user){
  global $db;
  $id="";
  $id_value="";

  //if already exists, delete it
  if (isset($user["id"]) and $user["id"]!="") {
    delUser($user["id"],false);
    $id="id,";
    $id_value=$user["id"].",";
  }
  if($user["type"]=="") $user["type"]=USER_STANDARD;
	if($user["lang"]=="") $user["lang"]="en";
  $password=$user["password"];
  if(trim($user['username'])=="") $user["password"]="";
  //insert the client credentials
  $qry="INSERT INTO cw_user ($id username,password,lang,type,entity_id) ";
  $qry.="VALUES ($id_value '".string2sql($user['username'])."','".string2sql($user['password'])."',";
  $qry.="'".string2sql($user['lang'])."','".$user['type']."','".$user['entity_id']."')";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0;
  else if ($user["id"]=="") $user["id"]=$db->Insert_ID();
  $ret=$user["id"];
  
  //refresh asterisk proxy manager users
  ast_writeProxyUsers();
  //refresh asterisk-stat users
  if(function_exists(ast_writeStatUsers)){
    ast_writeStatUsers();
  }
  //create LDAP entry
  if(function_exists("putLdapUser")){
    putLdapUser($user["id"],$password);
  }
  //create FAX user
  if(function_exists("putFaxUser")){
    $new_user=getUser($user['id']);
    if((is_array($new_user))&&(is_array($new_user[0]))){
      putFaxUser($new_user[0]);
    }
  }
  //return
  return $ret;
}

// ---- delUser(id:int):int --------------------------------------------------

function delUser($id,$reload=true){
  global $db;
  
  if((is_array($user))&&(is_array($user[0]))) $username=$user[0]['username'];

  //remove the user
  $qry="DELETE FROM cw_user WHERE id=".$id;
  $db->Execute($qry);
  
  //refresh asterisk proxy manager users
  ast_writeProxyUsers();
  //refresh asterisk-stat users
  if(function_exists(ast_writeStatUsers)){
    ast_writeStatUsers();
  }
  //delete fax user
  if(function_exists("delFaxUser")){
    removeFaxtoUser($id);
    delFaxUser($id);
  }
  if($reload===true){
    //delete Ldap user
    if(function_exists("delLdapUser")){
      delLdapUser($id);
    }
  }
  //return
  return 1;
}

// ---- checkUser(username:string, password:string):int ------------------------
// PS: password must be an md5 password
function checkUser($username, $password) {
  global $db;
  $ret=0;
  $u=getUser(0);
  if(is_array($u)){
    foreach($u as $user){
      if(($user["username"]==$username)&&(md5($user["password"])==$password)&&($user["active"]==1)){
        $ret=$user["id"];
        break;
      }
    }
  }
  return $ret;
}
// ---- activateDeactivateUser(id:int,switch:int):int --------------------------
function activateDeactivateUser($id,$switch){
  global $db;
  if(($id==0)||(trim($id)=="")) return 0;
  if(($switch!=0)&&($switch!=1)) return 0;
  $qry="UPDATE `cw_user` SET `active`='".$switch."' WHERE `id`='".$id."' LIMIT 1";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  return 1;
}
// ---- viewAstConfigFilename():string[] ---------------------------------------

function viewAstConfigFilename() {
  global $db;
  $qry="SELECT DISTINCT filename FROM ast_config";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) $ret[]=$arr["filename"];
  return $ret;
}

// ---- viewAstConfig(filename:string):string ----------------------------------

function viewAstConfig($f) {
  global $db;
  //extract file
  $qry="SELECT DISTINCT filename FROM ast_config WHERE filename='".string2sql($f)."'";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()) {
    $filename=$arr["filename"];
		//extract categories
    $qry="SELECT DISTINCT category,zsection FROM ast_config WHERE filename='".string2sql($filename)."' ORDER BY cat_metric,zsection";
    $res2=$db->Execute($qry);
    while($arr=$res2->FetchRow()) {
      $category=$arr["category"];
      $zsection=$arr["zsection"];
      $ret.= "\n";
      if($zsection!=""){
        $ret.= ";[".$zsection."]\n"; //added for Zapata
      }else{
        $ret.= "[".$category."]\n";
      }
      //extract parameters
      $qry="SELECT * ";
      $qry.="FROM ast_config ";
      $qry.="WHERE filename='".string2sql($filename)."' ";
      $qry.="AND category='".string2sql($category)."' ";
      $qry.="AND zsection='".$zsection."' ";
      $qry.="ORDER BY var_metric";
      $res3=$db->Execute($qry);
      while ($arr=$res3->FetchRow()) {
        $var_name=$arr["var_name"];
        $var_val=$arr["var_val"];
        $comment=$arr["comment"];
        $commented=$arr["commented"];
        if($var_name!="" and $commented!=1){
        	if(($zsection!="")&&($var_name=="channel")){
          	$ret.="$var_name => $var_val"; //added for Zapata
          }else{
        		$ret.="$var_name = $var_val";
          }
        }
        if($var_name!="" and $commented==1){
        	if(($zsection!="")&&($var_name=="channel")){
            $ret.="; $var_name => $var_val"; //added for Zapata
          }else{
            $ret.="; $var_name = $var_val";
          }
        }
        if($comment!="" and $var_name!="") $ret.= "\t ;$comment";
        if($comment!="" and $var_name=="") $ret.= ";$comment";
        $ret.="\n";
      }
    }
  }
  return $ret;
}

// ---- createAstConfig(lines:stringArray, filename:string):int ----------------
function createAstConfig($lines,$filename) {
  global $db;
  // remove all the old records about the file
  $qry="DELETE FROM ast_config WHERE filename='".string2sql($filename)."'";
  $res=$db->Execute($qry);
  // loop lines and insert text
  $cat_metric=-1;
  $var_metric=0;
  $zsection="";
  if(is_array($lines)){
    foreach($lines as $line_num => $line) {
      $line=trim($line);
      //empty line: skip
      if($line[0]=="") continue;
      //zsection: extract and save variable
      else if(($line[0]==";")&&($line[1]=="[")){
        $zsection=substr($line,2,strpos("]",$line)-1);
        continue;
      }
      //commented line: save as comment
      else if($line[0]==";"){
        $comment=str_replace("\"","'",trim(substr(trim($line),1)));
        $var_name="";
        $var_val="";
        $commented=1;
        $var_metric++;
      }
      //valid line: process it...
      else{
        $commented="0";
        //category
        if($line[0]=="["){
          $arr_comm=explode(";",trim($line));
          $category=substr(trim($arr_comm[0]),1,strlen(trim($arr_comm[0]))-2);
          $comment=trim($arr_comm[1]);
          $cat_metric++;
          $var_metric=-1;
          continue;
        }
        //parameter
        else{
          //$arr=split("=|=>",$line);   //DEPRECATED IN PHP 5.3
          $arr=preg_split("/=|=>/",$line);
          $var_name=trim($arr[0]);
          unset($arr[0]);
          $arr_comm=explode(";",trim(implode("=", $arr)));
          if(!is_array($arr_comm)) continue;
          else{
            $var_val=$arr_comm[0];
            $comment="";
            $alreadyCommented=false;
            for($i=0;$i<(count($arr_comm)-1);$i++){
              $sub_before=$arr_comm[$i];
              $sub_after=$arr_comm[($i+1)];
              if((strlen($sub_before)>0)&&($sub_before[(strlen($sub_before)-1)]=="\\")&&($alreadyCommented===false)){
                $var_val.=";".$sub_after;
              }else{
                if($comment=="") $comment.=$sub_after; else $comment.=";".$sub_after;
                $alreadyCommented=true;
              }
            }
          }
          $var_val=trim($var_val);
          $comment=trim($comment);
          $var_metric++;
        }
      }
      $qry="INSERT INTO ast_config (cat_metric,var_metric,commented,filename,category,var_name,var_val,comment,zsection)";
      $qry.="VALUES ($cat_metric,$var_metric,$commented,'".string2sql($filename)."','".string2sql($category)."',";
      $qry.="'".string2sql($var_name)."','".string2sql($var_val)."','".string2sql($comment)."','".string2sql($zsection)."')";
      $res=$db->Execute($qry);
      if($res==false) return 0;
    }
  }
  //return
  return 1;
}
// ---- getPublicKey():string ----------------
function getPublicKey() {
  global $db,$keyName, $voiceoneWSDocumentRoot;
  //$filename="/var/lib/asterisk/key/".$keyName.".pub";
  $filename=$voiceoneWSDocumentRoot."/sandbox/".$keyName.".pub";
  if(file_exists($filename)){
    $hi = fopen($filename,"r");
    $ret = fread($hi,filesize($filename));
    fclose($hi);
  } else $ret="[none]";
  return $ret;
}

// ---- stats(source:String, type:String, search:String, from:String, to:String, orderBy:String, page:Int ):CdrArray ----------------
/**
* Get CDR (Call Detail Record) Information
* @param array (source:String, type:String, search:String, from:String, to:String, orderBy:String, page:Int)
* @return CdrArray
* @todo add more information like Callstate (Answer, Failed, No Answer, wether this is incoming or outgoing), make entries per page dynamic via frontend request
* @todo add validation to date etc. so frontend cannot make query fail., add error codes
*/
function stats($request) {
  global $db;
  $ENTRIES_PER_PAGE=20;

  $source = trim($request["source"]);
  $type=$request["type"];
  $search=$request["search"];
  $from=$request["from"];
  $to=$request["to"];
  $orderBy=$request["orderBy"];
  $page=$request["page"];

  //where statement
  if ($source!="") {
    if (($type=="a" or $type=="r") ) $where.="OR  SUBSTRING(dstchannel FROM 1 FOR INSTR(dstchannel,'-')-1 )='$source' ";
    if (($type=="a" or $type=="r") and $search!="") $where.="AND src='$search' ";
    if (($type=="a" or $type=="d") ) $where.="OR  SUBSTRING(channel FROM 1 FOR INSTR(channel,'-')-1 )='$source' ";
    if (($type=="a" or $type=="d") and $search!="") $where.="AND dst='$search' ";
  } else {
    if ($search!="") $where.="AND (src='$search' or dst='$search') ";
  }
  if ($from!="") $where.="AND calldate>='$from' ";
  if ($to!="") $where.="AND calldate<='$to' ";

  //order by statment
  if ($orderBy=="dateDesc") $orderBy="ORDER BY calldate DESC";
  else if ($orderBy=="dateAsc") $orderBy="ORDER BY calldate";
  else if ($orderBy=="durationDesc") $orderBy="ORDER BY duration DESC";
  else if ($orderBy=="durationAsc") $orderBy="ORDER BY duration";

  //pagination
  if ($page=="") $page=1;
  $entries=$ENTRIES_PER_PAGE*(intval($page)-1);
  $limit=" LIMIT $entries,$ENTRIES_PER_PAGE";

  //Query Execution
  $where=trim(substr($where,3));
  if ($where!="") $where="WHERE ".$where;
  $qry="SELECT * FROM cdr $where $orderBy $limit";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $answer=substr($arr["dstchannel"],0,strrpos($arr["dstchannel"],"-"));
    $caller=substr($arr["channel"],0,strrpos($arr["channel"],"-"));
    if ($answer==$source) {
      if ($arr["clid"]=="") $arr["clid"]="Anonymous";
      $ret["received"]=$arr["clid"];
      $ret["dialed"]="";
      $ret["channel"]=$arr["channel"];
    }
    if ($caller==$source) {
      $ret["received"]="";
      $ret["dialed"]=$arr["dst"];
      $ret["channel"]=$arr["src"];
    }
    if ($source=="") {  //show all the calls
      if ($arr["clid"]=="") $arr["clid"]="Anonymous";
      $ret["received"]=$arr["clid"];
      $ret["dialed"]=$arr["dst"];
    }
    $ret["entity"]=$source;
    $ret["calldate"]=$arr["calldate"];
    $ret["duration"]=$arr["duration"];

    $result[]=$ret;
  }

  //error_log($qry);

  return $result;
}






//******************************************************************************
//******************               TIME SEGMENT             ********************
//******************************************************************************

// ---- getTimeSegment(id:int ):TimeSegmentArray -------------------------------
function getTimeSegment($id) {
  global $db;
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_time_segment $condition";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $ret[]=$v;
  }
  return $ret;
}

// ---- putTimeSegment(ts:TimeSegment ):int ------------------------------------
function putTimeSegment($ts) {
  global $db;
  // if modify, delete old timesegment
  if (isset($ts["id"]) and $ts["id"]!="") {
    delTimeSegment($ts["id"],false);
  }
  //insert new timesegment
  $qry="INSERT INTO cw_time_segment (id,name,date_from,date_to,hour_from,hour_to,week_day,day,month) ";
  $qry.="VALUES ('".$ts["id"]."','".string2sql($ts["name"])."','".string2sql($ts["date_from"])."','".string2sql($ts["date_to"])."',";
  $qry.="'".string2sql($ts["hour_from"])."','".string2sql($ts["hour_to"])."','".string2sql($ts["week_day"])."','".string2sql($ts["day"])."','".string2sql($ts["month"])."') ";
  $res=$db->Execute($qry);
  //return
  if($res===false) return 0; else return 1;

  /* OLD IMPLEMENTATION */
  /*
  if (is_array($ts))
  foreach($ts as $k=>$v) {
    if ($v!="|" and $v!="") {
      $fieldName.="`".$k."`,";
      $fieldValue.="'".string2sql($v)."',";
    }
  }
  $fieldName=substr($fieldName,0,-1);
  $fieldValue=substr($fieldValue,0,-1);
  $qry="INSERT INTO cw_time_segment ($fieldName) VALUES ($fieldValue)";
  $res=$db->Execute($qry);
  //error_log($qry);
  if ($ts["id"]=="") $ret=$db->Insert_Id();
  else $ret=$ts["id"];
  return $ret;
  */
}

// ---- delTimeSegment(id:int ):int --------------------------------------------
function delTimeSegment($id) {
  global $db;

  $qry="DELETE FROM cw_time_segment WHERE id=$id";
  $res=$db->Execute($qry);
  //error_log($qry);
  return 1;
}


//******************************************************************************
//******************               MACRO                   ********************
//******************************************************************************

// ---- getMacro(id:int):Macro[] -----------------------------------
/**
 * Get macro by id or get all macros if id is not defined
 *
 * @param integer macroID. If none specified, then all macros are returned
 * @return array macro[] using the database record structure
 * @see putMacro
 * @see getMacroByName
 * @see resolveMacroParam
 */

function getMacro($id) {
  global $db;

  //get the technology name and compute the Channel name
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_macro ".$condition." ORDER BY label";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $v['in_use']=isMacroInUse($v['id']);
    $ret[]=$v;
  }
  return $ret;
}

/**
 * Get macro by name
 *
 * @param string $name.
 * @return array macro[] using the database record structure
 * @see putMacro
 * @see getMacro
 * @see delMacro
 * @see resolveMacroParam
 */
function getMacroByName($name) {
  global $db;

  //get macro by name
  $qry="SELECT * FROM cw_macro WHERE name='".string2sql($name)."' ORDER BY label";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $v['in_use']=isMacroInUse($v['id']);
    $ret[]=$v;
  }
  return $ret;
}


// ---- putMacro(macro:Macro, keepID:Boolean):int -----------------------------------

/**
 * Add a macro to the system
 *
 * If the parameter array contains a value for ['id'] than putMacro tries to update the macro and keeps the id
 * @param array $macro macro:Macro
 * @param boolean $keepId default: true=keep Id regardless of the name; false=keep Id only if name matches
 * @return integer $entityId or 0 if failure
 * @see getMacro
 * @see getMacroByName
 * @see delMacro
 * @see resolveMacroParam
 */
function putMacro($macro, $keepId=true) {
  global $db,$realm;
  $entity=array();
  $ret=1;

  // Request for Update?
  // By name or by id?
  // Update via edit would be by id
  // Update via plugin would be by name
  // Otherwise we would risk to override a complete different entity via a plugin
  // So $keepId should be false if called via putPlugin.

  if ((isset($macro["id"])) and ($macro["id"] != "") and ($keepId === true)) {
    // id is set for new macro and must be used
      $entity["id"] = $macro["id"];
      delMacro($entity["id"], false);
  } elseif ((isset($macro["name"])) and ($macro["name"] != "") and ($keepId === false)) {
    // name is set for new macro and must be used
    $curMacro = getMacroByName($macro["name"]);
    if ($curMacro[0]['name'] == $macro['name']) {
      // name matches, so update is ok
      $entity["id"] = $curMacro[0]['id'];
      delMacro($entity["id"], false);
    }
  }

  //create a new entity
  $entity["type"]=MACRO;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;

  //insert the client credentials
  $name=$macro["name"];
  $label=$macro["label"];
  $description=$macro["description"];
  $parameter=$macro["parameter"];
  $code=$macro["code"];
  $qry="INSERT INTO cw_macro (id,name,label,description,code,parameter) ";
  $qry.="VALUES ($entity_id,'".string2sql($name)."','".string2sql($label)."',";
  $qry.="'".string2sql($description)."','".string2sql($code)."','".string2sql($parameter)."')";
  $res=$db->Execute($qry);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;

  //update the dialplan
  $before=true; $found=false; $after=false;
  $astconfig=viewAstConfig("extensions.conf");
  $arr=explode("\n",$astconfig);
  foreach($arr as $line) {
    if (trim($line)=="[macro-$name]") {$found=true; $before=false;}
    if ($before===true) $astconfig_before[]=$line;
    if (substr(trim($line),0,1)=="[" and trim($line)!="[macro-$name]" and $found===true) {$found=false; $after=true;}
    if ($after===true)  $astconfig_after[]=$line;
  }
  if($macro["code"])
  $codSplitted=explode("\n",$macro["code"]);
  if(!is_array($codSplitted)) $codSplitted=array();
  $arrCode=array_merge(Array("[macro-$name]"),$codSplitted);

  //$astconfig=array_merge($astconfig_before, $arrCode, $astconfig_after);

  // Assuming everything worked actually until here, $arrCode must be an array

  if (is_array($astconfig_before) && is_array($astconfig_after)) {
    $astconfig=array_merge($astconfig_before, $arrCode, $astconfig_after);
  } else {
    if (is_array($astconfig_before)) {
      $astconfig=array_merge($astconfig_before, $arrCode);
    } else {
      if(is_array($astconfig_after)){
        $astconfig=array_merge($arrCode, $astconfig_after);
      }else{
        $astconfig=$arrCode;
      }
    }
  }
  // If $astconfig is empty, it will delete ast_config section extensions.conf
  // Perhaps this should really tested in createAstConfig before deleting
  // everything accidentally !
  createAstConfig($astconfig,"extensions.conf");

  //return value
  if ($ret==1) $ret=$entity_id; else $ret=0;

  //if asterisk is ON, reload extensions
  $status=pbxSwitch("status");
  if($status=="ON") sendCommand("reload");

  return $ret;
}

// ---- delMacro(id:int):int --------------------------------------------------
/**
 * Delete macro by id
 *
 * @param integer macroID.
 * @param boolean $reload=true should asterisk perform a reload?
 * @return integer 1
 * @see putMacro
 * @see getMacro
 * @see getMacroByName
 * @see resolveMacroParam
 */

function delMacro($id,$reload=true) {
  global $db;

  //get the macro name
  $qry="SELECT * FROM cw_macro WHERE id=$id";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $name=$arr["name"];

  //remove the macro from the macro table
  $qry="DELETE FROM cw_macro WHERE id=".$id;
  $db->Execute($qry);

  //delete macro from ast-config
  $qry="DELETE FROM ast_config WHERE filename='extensions.conf' AND category='macro-".string2sql($name)."'";
  $db->Execute($qry);

  //remove the entity from the asterisk tables
  $ret=delEntity($id);

  //if asterisk is ON, reload extensions
  if ($reload===true){
    $status=pbxSwitch("status");
    if($status=="ON") sendCommand("reload");
  }

  return 1;
}
//
// ---- delMacro(id:int):int --------------------------------------------------
/**
 * Resolve macro parameter
 *
 * @param integer macroID
 * @param boolean string parameter list
 * @return string resolved parameter list
 * @see putMacro
 * @see getMacro
 * @see getMacroByName
 * @see delMacro
 */
function resolveMacroParam($macroId,$parameters){
  $ret = prepareMacro($macroId,$parameters);
  return $ret[1];
}
function isMacroInUse($macroId){
  global $db;
  if((trim($macroId)=='')||($macroId==0)) return 0;
  $qry="(";
  $qry.="SELECT id ";
  $qry.="FROM cw_rule_incoming ";
  $qry.="WHERE macro='".$macroId."' ";
  $qry.=") UNION ( ";
  $qry.="SELECT id ";
  $qry.="FROM cw_application ";
  $qry.="WHERE macro='".$macroId."' ";
  $qry.=")";
  $res=$db->Execute($qry);
  if($res!==false){
    $q=$res->RecordCount();
    if($q>0) return 1;
  }
  return 0;
}
//******************************************************************************
//******************               Version Check            ********************
//******************************************************************************

function versionCheck() {
  global $cw_version;

  $soapWsdl="";
  $soapWsdl = "http://root:voiceone@www.voiceone.it/admin/public/v1/?wsdl";
  $soapClient = new nusoapclient($soapWsdl, true);
  ini_set("soap.wsdl_cache_enabled", "1");
  $err = $soapClient->getError();
  if ($err) die('An error occurred while the WSDL was being accessed: '.$err);
  $v1server=$soapClient->getProxy();
  $v1server->setCredentials("root","voiceone");
  $v1server->useHTTPPersistentConnection();


  //pass the current VoiceOne version
  $entry=array(
    array("key"=>"version", "value"=>$cw_version)
  );

  //call the remote method
  $ret=$v1server->getLastVersion($entry);


  $ret[]=array("key"=>"current_version", "value"=>$cw_version);
  return $ret;
}

//******************************************************************************
//******************               I/O FUNCTION             ********************
//******************************************************************************
/**
* Reboot or shutdown the system
*
* @param string $action
* @return integer 1 = ok, 0 = error
*/
function shutdown($action) {
  global $voTools;
  if ($action=="reboot") $param="-r now";
  else if ($action=="halt") $param="-h now";
  else return 0;
  exec("sudo $voTools shutdown $param");
  // FIXME: log info
  return 1;
}
/**
* Start or Stop the PBX, or check its status
*
* @param string $action
* @return string ON = on, OFF = off, ERROR = error
*/
function pbxSwitch($action){
  global $voTools;
  if ($action=="start"){
    error_log("ASTERISK START");
    exec("sudo $voTools asterisk_start",$output,$retScript);
    return $output[0];
  }
  else if ($action=="stop"){
    error_log("ASTERISK STOP");
    exec("sudo $voTools asterisk_stop",$output,$retScript);
    return $output[0];
  }
  else if ($action=="status"){
    exec("sudo $voTools asterisk_status",$output,$retScript);
    return $output[0];
  }
  return "ERROR";
}
/**
* FUNCTION saveConfPerm: save permanently system configuration
*
* @param null
* @return integer 1 = ok, 0 = error
*/
function saveConfPerm() {
  global $voTools;
  exec("sudo $voTools saveConf");
  //return
  return 1;
}
//******************************************************************************
//******************               PRIVATE                  ********************
//******************************************************************************

/**
* Check wether an audiofile is used in any IVR
* Support Function. Scans IVR database for references to the audiofile
*
* @param string $filename
* @return integer 1 = used; 0 = not in use
*/
function isUsedIVRAudioFile($filename) {
  global $db;
  $qry="SELECT * FROM cw_ivr_node";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    while ($arr=$res->FetchRow()) {
      // IVR stores filenames in: audio, iaudio, taudio
      if (($arr["audio"] == $filename) or ($arr["iaudio"] == $filename) or ($arr["taudio"] == $filename)) {
	    // file is in use. exit.
		return 1;
      }
    }
  }

  // file is not in use.
  return 0;
}

/**
* Sync audiofiles in IVR directory with the database.
* Allows to copy files into the filesystem and get them into the database for later use.
*
* @return integer 1 = new files added to database; 0 = Error: Files are referenced in database, but do not exist in filesystem
*/
// FIXME: needs implementation and testing
function syncIVRAudioFilesWithDB() {
  global $db, $ivrStartingDir;
  $qry="SELECT * FROM cw_audio";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    while ($arr=$res->FetchRow()) {
	  return 1;
    }
  } else {
    return 1;
  }
}

//extract the base number from a list of numbers
function extractBaseNumber($numberString) {
  $numberArr=explode("|",$numberString);
  //errArr($numberArr);
  if (count($numberArr)>0) {
    $number=$numberArr[0];
    $numberLength=strlen($number);
    $ok=true;
    for ($l=0; $l<$numberLength; $l++){
      for ($i = 0; $i < count($numberArr); $i++) {
        if (substr($number,$l,1)==substr($numberArr[$i],$l,1)) $ok=$ok and true;
        else $ok=false;
      }
      if ($ok===true) $baseNumber.=$number[$l];
    }
  }
  if (count($numberArr)==0 or $baseNumber=="") $baseNumber="X";
  return $baseNumber;
}





function test() {
 $ret=sendCommand("reload");
 print_r($ret);
 return;
}

// ---- generateGlobalVar(groups:int[]): int
//create the global var in the Ast_Config

function generateGlobalVar($groups="") {
  global $db;
  //CREATE THE Channel VARIABLE
  $str="";
  $ret=getTrunk(0,0);
  if (is_array($ret))
  foreach($ret as $v) {
    $id=$v["id"];
    $str.=$v["name"]."&";
  }
  $app="TRUNK";
  $appdata=substr($str,0,-1);
  $resAstConfig=delAstConfig("extensions.conf","globals",$app);
  $resAstConfig=putAstConfig("extensions.conf","globals",$app,$appdata);
  //return
  return true;
}

// ---- putAstConfig($file:string, $context:string, $var:string, $var_data:string): int
function putAstConfig($file,$category,$var_name,$var_val,$comment="",$commented=0,$zsection=""){
  global $db;
  if ($file=="extensions.conf" and $asterikRealtime===true and $var_val!="Realtime/@extensions"){
    //**************************************************************************
    //                              ASTERISK REAL TIME
    //**************************************************************************

    //insert the realtime directive
    putAstConfig("extensions.conf", $category, "switch", "Realtime/@extensions");

    //extract the parameters
    //ereg ("^(.*),(.*),(.[^(]*)\((.*)\)$", $var_val, $regs); //DEPRECATED IN PHP 5.3
    preg_match("/^(.*),(.*),(.[^(]*)\((.*)\)$/", $var_val, $regs);
    $extension["context"]=$category;
    $extension["exten"]=$regs[1];
    $extension["priority"]=$regs[2];
    $extension["app"]=$regs[3];
    $extension["appdata"]=$regs[4];

    $table="extensions_table";
    $fields=$db->MetaColumnNames($table);
    foreach ($fields as $f) {
      if (array_key_exists($f,$extension) and $f!="id") {
        $field_list.="$f,";
        if (is_int($extension[$f])) $value_list.=$extension[$f].",";
        else  $value_list.="'".string2sql($extension[$f])."',";
      }
    }
    $field_list=substr($field_list,0,-1);
    $value_list=substr($value_list,0,-1);

    //insert the row
    $qry="INSERT INTO $table ($field_list) VALUES ($value_list)";
    $res=$db->Execute($qry);
    if ($res===false) $ret=0; else $ret=$db->Insert_ID();
    return $ret;

  }else{
    //**************************************************************************
    //                       ASTERISK STATIC - AST_CONFIG
    //**************************************************************************
    //if the same parameter entry is already into the table, do nothing else return entry id
    $qry="SELECT * FROM ast_config ";
    $qry.="WHERE filename='".string2sql($file)."' ";
    $qry.="AND category='".string2sql($category)."' ";
    $qry.="AND var_name='".string2sql($var_name)."' ";
    $qry.="AND var_val='".string2sql($var_val)."' ";
    $qry.="AND zsection='".string2sql($zsection)."' ";;
    $res=$db->Execute($qry);
    if(($res!==false)&&($arr=$res->FetchRow())){
      return $arr["id"];
    }
    //calculate the var_metric
    $qry="SELECT * FROM ast_config ";
    $qry.="WHERE filename='".$file."' ";
    $qry.="AND category='".$category."' ";
    $res=$db->Execute($qry);
    if($res===false) return 0;
    $count=$res->RecordCount();
    if($count>0){
      $arrCat=$res->FetchRow();
      $qry="SELECT MAX(var_metric) as maxvar FROM ast_config WHERE filename='".string2sql($file)."' AND category='".string2sql($category)."'";
      $res=$db->Execute($qry);
      if(($res!==false)&&($arr=$res->FetchRow())){
        $var_metric=$arr["maxvar"]+1;
        $cat_metric=$arrCat["cat_metric"];
      }else return 0;
    }else{
      $qry="SELECT MAX(cat_metric) as maxcat FROM ast_config WHERE  filename='".string2sql($file)."'";
      $res=$db->Execute($qry);
      if(($res!==false)&&($arr=$res->FetchRow())){
        $cat_metric=$arr["maxcat"]+1;
        $var_metric=0;
      }else return 0;
    }
    //insert in ast_config and get entry id
    $qry="INSERT INTO ast_config (cat_metric,var_metric,commented,filename,category,var_name,var_val,comment,zsection) ";
    $qry.="VALUES ('".string2sql($cat_metric)."','".string2sql($var_metric)."','".string2sql($commented)."','".string2sql($file)."',";
    $qry.="'".string2sql($category)."','".string2sql($var_name)."','".string2sql($var_val)."','".string2sql($comment)."','".string2sql($zsection)."')";
    $res=$db->Execute($qry);
    if($res===false) return 0;
    $astconfig_id=$db->Insert_ID();
    //Sort and Clean zapata channel section:
    // - force cat_metric to 0 and order var_metric by zsection
    // - force "channels" attribute to be the last of the zsection
    if(($file=="zapata.conf")&&($category=="channels")){
      //move other contexts away from cat_metric 0
      $qry="SELECT DISTINCT `category` FROM ast_config WHERE filename='zapata.conf' AND cat_metric='0' ";
      $res=$db->Execute($qry);
      if(($res!==false)&&($arr=$res->FetchRow())){
        $qry="UPDATE `ast_config` SET `cat_metric`='".string2sql($cat_metric)."' WHERE `category`='".string2sql($arr["category"])."'";
        $db->Execute($qry);
      }
      //sort zapata.conf and force "channels" attribute to be the last of the zsection
      $qry="SELECT `id`,`var_name`,`zsection` ";
      $qry.="FROM ast_config ";
      $qry.="WHERE filename='zapata.conf' ";
      $qry.="AND category='channels' ";
      $qry.="ORDER BY zsection, var_metric";
      $res=$db->Execute($qry);
      if($res!==false){
        $var_metric = 0;
        $zsection="";
        $ch=0;
        while($arr=$res->FetchRow()){
          if(trim($arr["var_name"])=="channel"){
            $ch=$arr["id"];
            continue;
          }
          if($zsection!=$arr["zsection"]){
            if($zsection!=""){
              $qry="UPDATE `ast_config` SET `cat_metric`='0',`var_metric`='".string2sql($var_metric)."' WHERE `id`='".string2sql($ch)."'";
              $db->Execute($qry);
              $var_metric++;
            }
            $zsection=$arr["zsection"];
          }
          $qry="UPDATE `ast_config` SET `cat_metric`='0',`var_metric`='".string2sql($var_metric)."' WHERE `id`='".string2sql($arr["id"])."'";
          $db->Execute($qry);
          $var_metric++;
        }
        if($ch!=0){
          $qry="UPDATE `ast_config` SET `cat_metric`='0',`var_metric`='".string2sql($var_metric)."' WHERE `id`='".string2sql($ch)."'";
          $db->Execute($qry);
        }
      }
    }
    //return
    return $astconfig_id;
  }
}
// --- write additional parameters to delete settings of previous zsection
function optimizeAstConfig($tech){
  global $db;
  if($tech==ZAP){
    $techArr=getTechnology($tech);
    if((!is_array($techArr))||(!is_array($techArr[0]))) return false;
    $file=$techArr[0]["config_file"];
    $category=$techArr[0]["general_context"];
    //delete optimized options
    $qry="DELETE FROM ast_config ";
    $qry.="WHERE filename='".string2sql($file)."' ";
    $qry.="AND category like '".string2sql($category)."' ";
    $qry.="AND comment like '".string2sql("automatlicty inserted to ovewride prevoius settings")."' ";
    $res=$db->Execute($qry);
    //get ast_config
    $defaultSetting=array();
    $settings=array();
    $qry="SELECT zsection,var_name,var_val ";
    $qry.="FROM ast_config ";
    $qry.="WHERE filename='".string2sql($file)."' ";
    $qry.="AND category like '".string2sql($category)."' ";
    $qry.="ORDER BY var_metric ";
    $res=$db->Execute($qry);
    if($res!==false){
      $num=-1;
      $zsection="";
      while($arr=$res->FetchRow()){
        if(trim($arr["var_name"])=="") continue;
        if(trim($arr["zsection"])==""){
          $defaultSetting[$arr["var_name"]]=$arr["var_val"];
        }else{
          if($arr["zsection"]!=$zsection){$zsection=$arr["zsection"];$num++;}
          $settings[$num][]=array(
              "zsection" => $zsection,
              "var_name" => $arr["var_name"],
              "var_val" => $arr["var_val"]
            );
        }
      }
    }
    //overwrite params of previous zsection with defalut value
    for($i=1;$i<count($settings);$i++){
      //previous zsections option array
      $tmp=array();
      for($j=0;$j<$i;$j++){
        $tmp=array_merge($tmp,$settings[$j]);
      }
      $prevSettings=array();
      for($j=0;$j<count($tmp);$j++){
        $prevSettings[$tmp[$j]["var_name"]]=$tmp[$j]["var_val"];
      }
      //zsection options array
      $zsection=$settings[$i][0]["zsection"];
      $zsectionArr=array();
      for($j=0;$j<count($settings[$i]);$j++){
        $zsectionArr[$settings[$i][$j]["var_name"]]=$settings[$i][$j]["var_val"];
      }
      //option arrays diff
      $diffArr=array_diff_key($prevSettings,$zsectionArr);
      if(is_array($diffArr)){
        foreach($diffArr as $var_name=>$var_val){
          //retrieve defaul value in default settings
          if((is_array($defaultSetting))&&(array_key_exists($var_name,$defaultSetting))){
            $def_val=$defaultSetting[$var_name];
          }
          //retrive default value for option in zapata options
          else{
            $option=getOptionByName($var_name,"ZAP");
            $def_val=$option[0]["default"];
          }
          putAstConfig($file,$category,$var_name,$def_val,"automatlicty inserted to ovewride prevoius settings",0,$zsection);
        }
      }
    }
    return true;
  }
}
// ---- delAstConfig($file:string, $context:string, $var:string): bool
function delAstConfig($file,$category,$var_name="",$var_val="",$zsection="") {
  global $db;
  //check input parameters
  if ($category=="" or $file=="") return false;
  //remove from ast_config table
  $qry="DELETE FROM ast_config ";
  $qry.="WHERE filename='".string2sql($file)."' ";
  $qry.="AND category like '".string2sql($category)."' ";
  $qry.="AND zsection='".string2sql($zsection)."' ";
  if($var_name!="") $qry.=" AND var_name like '".string2sql($var_name)."'";
  if($var_val!="") $qry.=" AND var_val like '".string2sql($var_val)."'";
  $res=$db->Execute($qry);
  return $res;
}

/**
* PRIVATE FUNCTION: msns2string
* convert an array of msns in a pipe separated string
* @param msns:StringArray -> array of msns numbers
* @return:String -> pipe separated string of number
*/
function msns2string($msns) {
	$msnsString="";
  if(is_array($msns)){ 
    foreach($msns as $m){
    	$m=trim($m);
      if($m!=""){
        if(strpos($m,"-")!==false) {
          $arr=explode("-",$m);
          $numberLen=strlen($arr[0]);
          for($i=$arr[0];$i<=$arr[1];$i++){
            $msnsString.=str_pad($i, $numberLen, "0", STR_PAD_LEFT)."|";
          }    
        }else{
        	$msnsString.=$m."|";
        }
      }
    }
    if($msnsString!="") $msnsString=substr($msnsString,0,-1);
	}
  return $msnsString;
}

  if (!function_exists("file_put_contents")) {
     function file_put_contents($filename,$content,$mode="w") {
       if(!$file = @fopen($filename, $mode)) return false;
       if($file) {
         if(!@fwrite($file,$content)) return false;
         fclose($file);
         @chmod($file, 0755);
       }
       return true;
     }
  }

  if (!function_exists("file_get_contents")) {
    function file_get_contents($filename, $use_include_path = 0) {
      $data = ""; // just to be safe. Dunno, if this is really needed
      $file = @fopen($filename, "rb", $use_include_path);
      if ($file) {
        while (!feof($file)) $data .= fread($file, 1024);
        fclose($file);
      }
      return $data;
    }
  }

  if (!function_exists("array_diff_key")) {
    function array_diff_key() {
      $arrs = func_get_args();
      $result = array_shift($arrs);
      foreach ($arrs as $array) {
          foreach ($result as $key => $v) {
              if (array_key_exists($key, $array)) {
                  unset($result[$key]);
              }
          }
      }
      return $result;
    }
  }

/**
 * Convert an XML data structure that is *read from file* into an array.
 *
 * Array stucture: Node:[0]['name]=TAG opened; [0]['text']=Value [0]['closetag']=TAG closed; [0]['elements']=Array of subnode
 * @param string $xml filename with xml data to read and convert.
 * @return array $xmlary reflecting structure
 * @todo separate file_get_contents from the function. This function should not read the file but take only the the xml string!
 */
  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;
    }
  }

  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;
    }
  }

  //convert a string in a sql compatible string
  function string2sql($txt){
    if(get_magic_quotes_runtime()==0){ //get_magic_quotes_gpc()
      //$txt=str_replace("'","\\'",$txt);
      $txt=addslashes(''.$txt);
    }
    return $txt;
  }
  //recursive array search for multidimensional array
  function array_key_exists_recursive($needle,$haystack){
  	if(is_array($haystack)){
      foreach($haystack as $key=>$val) {
        if(is_array($val)){ if($ret=array_key_exists_recursive($needle,$val)) return $ret; }
        else if($val == $needle) return true;
      }
    }
    return false;
  }
  /* OLD IMPLEMENTATION
  function array_key_exists_recursive($needle,$haystack,$id=0){
    foreach($haystack as $key=>$val) {
      if(is_array($val)) { if($ret=array_key_exists_recursive($needle,$val,$key)) return $ret; }
      elseif($val == $needle) return $id;
    }
    return 0;
  }
  */



/**
 * CALL FINDER
 */
/*
function setTarget($name,$number,$description) {
  global $db;
  $qry="INSERT INTO `rubrica` (`name` , `number` , `description`) ";
  $qry.="VALUES ('".string2sql($name)."', '".string2sql($number)."', '".string2sql($description)."'); ";
  $res=$db->Execute($qry);
  if($res===false) return 0; else return 1;
}
function delTargets() {
  global $db;
  $qry="TRUNCATE TABLE `rubrica` ";
  $res=$db->Execute($qry);
  $qry="TRUNCATE TABLE `cdr` ";
  $res=$db->Execute($qry);
  $qry="TRUNCATE TABLE `incomingcall` ";
  $res=$db->Execute($qry);
  if($res===false) return 0; else return 1;
}
function getTargetCalls() {
  global $db;
  $qry="SELECT incomingcall.`id`,rubrica.`name`,rubrica.`number`,rubrica.`description`,cdr.`disposition` ";
  $qry.="FROM rubrica JOIN incomingcall ON rubrica.`number` = incomingcall.`caller_number` ";
  $qry.="LEFT OUTER JOIN cdr ON incomingcall.`id` = cdr.`uniqueid` ";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()) {
    if((!isset($arr["disposition"]))||($arr["disposition"]=="")) $arr["disposition"]="INCOMING";
    $ret[]=$arr;
  }
  return $ret;
}
*/


?>
