<?
/**
 * 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;
  //syncEntities();
  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"];
  $key=$ent["key"];
  if ($key=="") $key="0";
  if ($ent["id"]!="") {
    $id=$ent["id"];
    $oldentity=getEntity($id);
    if (!isset($oldentity[0])) $qry="INSERT INTO cw_entity (`id`,`type`,`key`) VALUES ($id, $type, $key)";
    else $qry="UPDATE cw_entity SET `type`=$type, `key`=$key WHERE id=$id";
  } else 
    $qry="INSERT INTO cw_entity (`type`,`key`) VALUES ($type, $key)";

  $res=$db->Execute($qry);
  if ($res===false) $ret=0; 
  else if ($ent["id"]!="") $ret=$ent["id"];
    else $ret=$db->Insert_ID();

  sendCommand("reload");

  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:
      $entityArray=getProvider($entity_id);
      $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 ----------------------------------------------
//private
function getMetaGroupFromEntity($entity_id) {
  $entity=getEntity($entity_id);
  $entityType=$entity[0]["type"];
  switch ($entityType) {
    case EXTENSION:
      $ret=GROUP_OF_EXTENSION;
    break;
    case PROVIDER:
    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;
}

// ---- 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,$groupPrefix;

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


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

// ---- getClient(id:int, technology:int):client[] -----------------------------------

function getClient($id,$technology=0) {
  global $db;

  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"];

    $ret[]=$v;
  }

  return $ret;
}

// ---- putClient(client:Client):int -----------------------------------

function putClient($client) {
  global $db,$realm;
  $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"]);
    //retrive the already binded user, if exists, to maintain the old user id
    $resUsr=$db->Execute("SELECT * FROM cw_user WHERE entity_id=".$client["id"]);
    while ($arru=$resUsr->FetchRow()) $userId=$arru["id"];
    //delete client
    delClient($client["id"],false);
    $entity["id"]=$client["id"];
  }
  
  //create a new entity
  $entity["type"]=EXTENSION;
  $entity["key"]=0;
  $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,\"{$client['name']}\",\"{$client['surname']}\",\"{$client['extension']}\",\"{$client['alias']}\",\"{$client['username']}\",\"{$client['password']}\",$rule,{$client['technology']})";
  $res=$db->Execute($qry);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  
  //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"];
  if ($client["technology"]==MISDN) {
    $portsOptionArr=getOptionByName("ports","mISDN");
    $portsOptionId=$portsOptionArr[0]["id"];
    foreach ($client["options"] as $k=>$v) 
      if ($v["key"]==$portsOptionId) $port=$v["value"];
    $channel['resource']=$port."/".$client["extension"];
  } else $channel['resource']=$client["extension"];
  $channel["rule"]=$rule;
  $channel['key']=$entity_id;
  $channelId=putChannel($channel);  
  
  //help text for viewAstConfig
  putAstConfig("extensions.conf", $context, "", "", $extension." - ".$client["name"]." ".$client["surname"]." - ".$channelName);
  //Hint
  putAstConfig("extensions.conf", $context, "exten", "$extension,hint,$channelName");

  putAstConfig("extensions.conf", $context, "exten", "$extension,1,AGI(dial.php|entity=$entity_id&group=".GROUP_OF_EXTENSION."&extension=$extension)");
  if ($alias!="") putAstConfig("extensions.conf", $context, "exten", "$alias,1,Goto,$extension|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,"type"=>USER_STANDARD, "entity_id"=>$entity_id, "lang"=>$client["language"]));

  //insert the voicemail
  if (isset($client["voicemail"])) $ret=putVoiceMail($client["voicemail"]);
  
  //reload the extensions
  sendCommand("reload");

  return $ret;
}



// ---- delClient(id:int):int --------------------------------------------------

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

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

  //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 
  $qry="DELETE FROM cw_user WHERE entity_id=".$entity_id;
  $db->Execute($qry);

  //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 ($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);
  
  //reload the extensions
  if ($reload==true) sendCommand("reload");
  
  return $ret;
}

//******************************************************************************
//******************               OFFICE                   ********************
//******************************************************************************

// ---- getOffice(id:int):department[] -----------------------------------

function getOffice($id) {
  global $db;

  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_office ".$condition." ORDER BY name";
  error_log($qry);
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) {
    //retrieve the numbers
    $v["numbers"]=split("\|",$v["numbers"]);

    //retrieve the provider specific options
    $v["options"]=getEntityOption($id);
      
    //retrieve the client 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"];

    $ret[]=$v;
  }
  return $ret;
}

// ---- putOffice(dep:Department):int -----------------------------------

function putOffice($dep) {
  global $db,$voiceoneWSDocumentRoot,$voTools,$keyName,$outgoing_prefix;
  $entity=array();
  $ret=1;

  //if already exists, delete it!
  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["key"]=0;
  $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);
  
  //if speeddial is blank, calculate it!
  if ($dep["speeddial"]=="") $dep["speeddial"]=getNextSpeedDial();
  
  //insert the client credentials
  $name=$dep["name"];
  $speeddial=$dep["speeddial"];
  $username=$dep["username"];
  $password=$dep["password"];
  $inkey=$dep["inkey"];
  $host=$dep["host"];
  $port=$dep["port"];
  $register=$dep["register"];
  $did=$dep["did"];
  if (is_array($dep["numbers"])) $numbers=implode("|",$dep["numbers"]);
  $qry="INSERT INTO cw_office (id,name,speeddial,username,password,inkey,host,port,register,did,numbers,rule) ";
  $qry.="VALUES ($entity_id,'$name','$speeddial','$username','$password','$inkey','$host','$port','$register','$did','$numbers',$rule)";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0; else $ret=1;
  
  //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']=$did;
  if ($did==1) $channel['msns']=Array("X");
  else $channel['msns']=null;
  $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='trunk_".$channelId."' WHERE name='".$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 phonenumbers in the ast_config
  $context="trunk_out_".$channelId;
  if (is_array($dep["numbers"]))
  foreach ($dep["numbers"] as $n) {
    putAstConfig("extensions.conf",$context,"exten",$outgoing_prefix.$n.",1,AGI(dial.php|entity=$entity_id&group=".GROUP_OF_OFFICE."&extension=".$outgoing_prefix.$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.")");
*/  
    
  //return value
  if ($ret==1) $ret=$entity_id; else $ret=0;

  //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);

  //reload the extensions
  sendCommand("init keys");
  sendCommand("reload");

  return $ret;
}

// ---- delOffice(id:int):int --------------------------------------------------

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

  //get the technology of the provider
  $resArr=getOffice($entity_id);
  $office=$resArr[0];


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

  //remove client specific options
  $ret=delEntityOption($entity_id,IAX2);

  //delete the Channel
  $channelId=getChannelFromEntity($entity_id);
  if ($channelId!=0) delChannel($channelId);

  //remove the office from voiceone aux table
  $qry="DELETE FROM cw_office WHERE id=".$entity_id;
  $db->Execute($qry);

  //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");
  
  //remove the entity
  $ret=delEntity($entity_id);
  
  //reload the extensions
  if ($reload===true) sendCommand("reload");
  
  return $ret;
}

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

// ---- getProvider(id:int):Provider[] -------------------------------

function getProvider($id) {
  global $db;

  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_provider $condition";
  $res=$db->Execute($qry);
  while ($v=$res->FetchRow()) {
    $id=$v["id"];
    //retrieve the provider specific options
    $v["options"]=getEntityOption($id);
    //retrieve the phone numbers
    $channel=getChannel(getChannelFromEntity($id));
    //$v["numbers"]=split("\|",$channel[0]["msns"]);
    $v["numbers"]=$channel[0]["msns"];
    $v["did"]=$channel[0]["did"];
    $ret[]=$v;
  }


  return $ret;
}

// ---- putProvider(res:Provider):int -----------------------------------

function putProvider($res) {
  global $db;

  //initialize some variable
  $entity=array();

  //modify? delete and create a new entry
  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"];

    delProvider($res["id"],false);
    $entity["id"]=$res["id"];
  }

  //create a new entity
  $entity["type"]=PROVIDER;
  $entity["key"]=0;
  $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"]);
  
  //if speeddial is blank, calculate it!
  if ($res["speeddial"]=="") $res["speeddial"]=getNextSpeedDial();
  
  //insert the provider credentials
  $qry="INSERT INTO cw_provider (id,name,speeddial,username,password,host,port,technology,rule) ";
  $qry.="VALUES ($entity_id,\"{$res['name']}\",\"{$res['speeddial']}\",\"{$res['username']}\",\"{$res['password']}\",\"{$res['host']}\",\"{$res['port']}\",\"{$res['technology']}\",$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
    putEntityOption($res["options"], $res["technology"], $entity_id, $res);

    //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"];
    $channelId=putChannel($channel);  

    //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"]."'";
      $db->Execute($qry);
    } else {
      $res=delAstConfig($techFile, $res["name"], "context");
      $res=putAstConfig($techFile, $res["name"], "context", "trunk_".$channelId);
    }
    
    //register the provider 
    if ($res["technology"]==SIP)  putAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["name"]);
    if ($res["technology"]==IAX2) putAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["host"]);
  }
  
  //reload the extensions
  sendCommand("reload");

  return $entity_id;
}

// ---- delProvider(id:int):int --------------------------------------------------

function delProvider($entity_id,$reload=true) {
  global $db;
    
  //get the technology of the provider
  $resArr=getProvider($entity_id);
  $res=$resArr[0];

  //get the tech infos
  $techArr=getTechnology($res["technology"]);  
  $techRealTime = $techArr[0]["realtime"];
  $techTable    = $techArr[0]["table"];
  $techFile     = $techArr[0]["config_file"];
  
  //remove client specific options
  $ret=delEntityOption($entity_id,$res["technology"]);

  //delete the Channel
  $channelId=getChannelFromEntity($entity_id);
  if ($channelId!=0) delChannel($channelId);

  //remove the entry from the resource table
  $qry="DELETE FROM cw_provider 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"]);
  if ($res["technology"]==IAX2) delAstConfig($techFile, "general", "register", $res["username"].":".$res["password"]."@".$res["host"]);

  //remove the resource from the asterisk tables
  $ret=delEntity($entity_id);
  
  //reload the extensions
  if ($reload===true) sendCommand("extensions reload");
  
  return $ret;
}


// ---- 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;
    $v["msns"]=split("\|",$v["msns"]);
    $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"]=split("\|",$v["msns"]);
    $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) {
  global $db;
  
  //retrieve the current order number
  $qry="SELECT `order` FROM cw_channel WHERE id='$id'";
  $res=$db->Execute($qry);
  $arr = $res->FetchRow();
  $idFrom=$id;
  $orderFrom=$arr["order"];
  
  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; 
  
  $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 the general global variable
  generateGlobalVar();

  //reload the extensions
  sendCommand("extensions reload");
  
  if ($res===false) return new soap_fault('Server','','Ivr move error, mysql UPDATE command failed');
  else return 1;
}

// ---- putChannel(Channel:Channel):int -----------------
function putChannel($channel) {
  global $db;

  //if already exists, delete it 
  if (isset($channel["id"]) and $channel["id"]!="") {
    delChannel($channel["id"],false);
    $entity["id"]=$channel["id"];
  }

  $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"];
  if ($key=="") $key="0";
  if ($order=="") $order="0";
  if ($trunk=="") $trunk="0";
  if ($rule=="")  $rule="0";
  
  //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);
  
  //create a new entity
  $entity["type"]=CHANNEL;
  $entity["key"]=$key;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  
  //some checks
  if ($speedDial=="") $speedDial=getNextSpeedDial();
  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`) VALUES ($entity_id, '$channelName', $technology, $key, '$msnsString','$did','$rule','$speedDial',$order,$trunk)";
  $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|answered=\${EXTEN}&channel=$entity_id&rule=$rule)");
      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|answered=\${EXTEN}&channel=$entity_id&rule=$rule)");
    } else { 
    // DID DISABLED - list all the available numbers
      $numbers=split("\|",$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|answered=$n&channel=$entity_id&rule=$rule)");
        }
      }
    }
        
    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|answered=\${EXTEN}&channel=$entity_id&rule=$rule)");
    //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=".$channel["key"]."&group=".$metaGroup."&extension=".$speedDial."*)");
    if ($metaGroup==GROUP_OF_OFFICE) 
      putAstConfig("extensions.conf",$context,"exten",$speedDial."*,1,AGI(dial.php|entity=".$channel["key"]."&group=".$metaGroup."&extension=".$speedDial."*)");
    addEntityToGroup($entity_id,array());
  }

  //regenerate globals var
  generateGlobalVar();
  
  //reload the extensions
  if ($key=="" or $key=="0") sendCommand("reload");
  
  return $entity_id;
}

// ---- delChannel(id:int):int ----------------------------------

function delChannel($id, $reload=true) {
  global $db;
  
  $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='$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='$context'");

  //remove the resource from the asterisk tables
  removeEntityFromGroup($id);

  //regenerate globals var
  generateGlobalVar();

  //reload extensions
  if ($reload===true) sendCommand("extensions reload");
  
  return 1;
}

// ---- getNextSpeedDial():int -----------------------------------
//private 
function getNextSpeedDial() {
  global $db;
  $max=0;
  $qry="SELECT * FROM cw_channel";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()) 
    if ( intval($arr["speeddial"])>$max) $max=intval($arr["speeddial"]);

  $max++;
  return $max;
}

//******************************************************************************
//******************               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
    $v["number"]=split("\|",$v["number"]);

    //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 $ret;
}

// ---- putPhoneNumber(number:PhoneNumber):int -----------------------------------

function putPhoneNumber($number) {
  global $db, $outgoing_prefix;

  $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["key"] =0;  //key is zero. The NUMBER entity hasn't a reference table, so no key has to be written
  $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,emergency) ";
  $qry.="VALUES ($entity_id,\"$name\",\"$pn\",$emergency)";
  $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) {
    $n=str_replace("_","_".$outgoing_prefix,$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;

  //reload the extensions
  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='$name'");

    //remove all the client inclusions    
    $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;
}

// ---- 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`) VALUES ($id_value '$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;

  //reload the extensions
  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
  $res=delAstConfig("extensions.conf",$name);
  $res=delAstConfig("extensions.conf","%","include",$name);
  
  return 1;
}



//******************************************************************************
//******************               QUEUE                   *********************
//******************************************************************************

// ---- getQueue(id:int):Queue[] -----------------------------------------------
/**
 * Get Queue by id or get all Queues if id is not defined. 
 * 
 * @param integer queueID. If none specified, then all queues are returned
 * @return array queue[] using the database record structure including a list of all members for each queue as array in field "member"
 * @see putQueue
 * @see delQueue
 * @see getQueueByName
 * @todo fix group export to be xml compliant (do not use numeric tags)
 */

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

    //extract the attributes from the ast_config
    $qry="SELECT * FROM ast_config WHERE filename='queues.conf' AND category='$name'";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) 
      if ($arr["var_name"]!="member") $arr[$arr2["var_name"]]=$arr2["var_val"];

    //extract the member from the queue_member table
    unset($member);
    $qry="SELECT member_id FROM cw_queue_member WHERE queue_id=$queue_id";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) $member[]=$arr2["member_id"];
    $arr["member"]=$member;
    $ret[]=$arr;
  }
  return $ret;
}

// ---- getQueuebyName($name:string):Queue[] -----------------------------------------------

/**
 * Get Queue by name 
 * 
 * @param string $name.
 * @return array queue[] using the database record structure including a list of all members for each queue as array in field "member"
 * @see putQueue
 * @see delQueue
 * @see getQueue
 * @todo fix group export to be xml compliant (do not use numeric tags)
 */
function getQueueByName($name) {
  global $db;
  $count=0;  
  $qry="SELECT * FROM cw_queue WHERE name = '$name' ORDER BY name";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    $queue_id=$arr["id"];
    $name=$arr["name"];

    //extract the attributes from the ast_config
    $qry="SELECT * FROM ast_config WHERE filename='queues.conf' AND category = '$name'";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) 
      if ($arr["var_name"]!="member") $arr[$arr2["var_name"]]=$arr2["var_val"];

    //extract the member from the queue_member table
    unset($member);
    $qry="SELECT member_id FROM cw_queue_member WHERE queue_id=$queue_id";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) $member[]=$arr2["member_id"];
    $arr["member"]=$member;
    $ret[]=$arr;
  }
  return $ret;
}

// ---- putQueue(queue:Queue):int ----------------------------------------------

/**
 * Create a new Queue or recreate the queue it it exists. Update Asterisk afterwards.
 * 
 * @param array $queue  (queue:Queue)
 * @param boolean $keepId default: true=keep Id regardless of the name; false=keep Id only if name matches
 * @return integer queueID 
 * @see putQueue
 * @see delQueue
 */
function putQueue($queue, $keepId=true) {
  global $db;
     
  // 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.
  
  //go to the alterQueue function
  if ((isset($queue["id"])) and ($queue["id"] != "") and ($keepId == true)) {
    // id is set for new queue and must be used       
      $entity["id"] = $queue["id"];
      delQueue($entity["id"]);      
  } elseif ((isset($queue["name"])) and ($queue["name"] != "") and ($keepId == false)) {
    // name is set for new queue and must be used
    $curQueue = getQueueByName($queue["name"]);
    if ($curQueue[0]['name'] == $queue['name']) {
      // name matches, so get the id of the existing queue
      $entity["id"] = $curQueue[0]['id'];
      delQueue($entity["id"]);      
    }
  }

  //create a new entity. use the existing id if this is an update
  $entity["type"]=QUEUE;
  $entity["key"] =0;  //key is zero. The NUMBER entity hasn't a reference table, so no key has to be written
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  
  $ret=1;
  $name=$queue["name"];
  $qry = "INSERT INTO cw_queue (`id`,`name`) VALUES ($entity_id,'$name')";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Queue creation error, mysql INSERT command failed'); 

  $queue["timeout"]="15";
  
  //insert the queue property in the ast_config table  
  foreach ($queue as $k=>$v) {
    if ($k!="id" and $k!="member" and $k!="name") {
      $res=putAstConfig("queues.conf", "$name", $k, $v);
    }
  }  
  
  //insert the members into the queue in the ast_config
  $res=memberToQueue($entity_id, $queue["member"]);
  if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;

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

  //reload the extensions
  sendCommand("reload");

  return $ret;
}

// ---- delQueue(id:int):int --------------------------------------------------
/**
 * Delete a queue by ID
 * 
 * @param integer $queueID
 * @return integer 1 
 * @see getQueue
 * @see putQueue
 */

function delQueue($id) {
  global $db;
  
  //retrieve the queue name
  $q=getQueue($id);
  $name=$q[0]["name"];

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

  //delete entry from the support table 
  $qry="DELETE FROM cw_queue WHERE id='$id'";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Queue remove error, mysql DELETE command failed');

  //delete entry from the support table
  $qry="DELETE FROM cw_queue_member WHERE queue_id='$id'";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Queue-member remove error, mysql DELETE command failed');

  //remove the queue related entry in the ast_config table
  $res=delAstConfig("queues.conf",$name);
  
  return 1;
}

// ---- memberToQueue(queue:int, member:int[]):int ---------------------------
//      this function is private

function memberToQueue($queue_id,$member) {
  global $db;
  $ret=1;
  $queue=getQueue($queue_id);
  $queueName=$queue[0]["name"];
  $qry="DELETE FROM cw_queue_member WHERE `queue_id`=$queue_id";
  $res=$db->Execute($qry);
  
  //$qry="DELETE FROM ast_config WHERE `filename`='queues.conf' AND `category`='$queueName' and var_name='member'";
  //$res=$db->Execute($qry);
  $res=delAstConfig("queues.conf",$queueName,"member");


  if (is_array($member))
  foreach ($member as $member_id) {
    $qry="INSERT INTO cw_queue_member (`queue_id`,`member_id`) VALUES ($queue_id, $member_id)";
    $res=$db->Execute($qry);
    if ($res===false) return false;

    /*
    $m=getClient($member_id);
    $memberName=$m[0]["peer"]["name"];
    $memberProtocol=$m[0]["peer"]["protocol"];
    if ($memberProtocol==SIP_PROTOCOL) $res=putAstConfig("queues.conf","$queueName","member","SIP/".$memberName);
    if ($memberProtocol==IAX_PROTOCOL) $res=putAstConfig("queues.conf","$queueName","member","IAX/".$memberName);
    */
    $channelArr=getChannel(getChannelFromEntity($member_id));
    $channelName=$channelArr[0]["name"];
    $res=putAstConfig("queues.conf","$queueName","member",$channelName);    
    
    if ($res===false) $ret=$ret and 0; else $ret=$ret and 1;
  }    
  return $ret;
}


//******************************************************************************
//******************               VOICE MAIL              *********************
//******************************************************************************

// ---- getVoiceMail(id:int):VoiceMail[] ---------------------------------------
function getVoiceMail($id) {
  global $db;
  $count=0;
  if ($id!="") $condition="WHERE mailbox=$id ";
  $qry="SELECT * FROM voicemail_users $condition ORDER BY mailbox";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    $arr["id"]=$arr["uniqueid"];
    $ret[]=$arr;
  }
  return $ret;
}

// ---- putVoiceMail(voicemail:VoiceMail):int ---------------------------------------
function putVoiceMail($voicemail) {
  global $db;
  $id="";
  $id_value="";
  
  //if already exists, delete it
  if (isset($voicemail["mailbox"]) and $voicemail["mailbox"]!="") {
    delVoiceMail($voicemail["mailbox"],false);
  } else return 0; //no mail box is defined
  
  //insert the mailbox
  $qry="INSERT INTO voicemail_users (context, mailbox, password, fullname, email, pager, attach, saycid, hidefromdir) ";
  $qry.="VALUES ('default', \"{$voicemail['mailbox']}\",\"{$voicemail['password']}\",\"{$voicemail['fullname']}\",\"{$voicemail['email']}\",\"{$voicemail['pager']}\",\"{$voicemail['attach']}\",\"{$voicemail['saycid']}\",\"{$voicemail['hidefromdir']}\")";
  $res=$db->Execute($qry);
  
  if ($res===false) $ret=0; 
  else return $voicemail["mailbox"]; 
}
  
// ---- delVoiceMail(id:int):int ---------------------------------------
function delVoiceMail($mailbox) {
  global $db;
  $qry="DELETE FROM voicemail_users WHERE mailbox=$mailbox";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0; 
  else return 1;
}


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

// ---- getCard(id:int):Card[] -----------------------------------------------

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


// ---- putCard(id:int):int -----------------------------------------------
// you can add an isdn card by simply passing the id of the card type.

function putCard($id) {
  global $db;

  //read how many ports are already installed
  $qry="SELECT * FROM cw_isdn_port";
  $res=$db->Execute($qry);
  $installedPort=$res->RecordCount();

  //read how many ports has the choosed card
  $qry="SELECT * FROM cw_isdn_card WHERE id=$id";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $ports=$arr["ports"];
  
  //read the id of the previous installed card 
  $qry="SELECT MAX(card_id) as lastid FROM cw_isdn_port";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $card_id=$arr["lastid"];
  if ($card_id!="NULL") $card_id++; else $card_id=1;
  
  $res=true;
  for ($i=1; $i<=$ports; $i++) {
    $qry="INSERT INTO cw_isdn_port (port,layermask,protocol,card,card_id) VALUES ($i,'0xf','0x2',$id,$card_id)";
    $res&=$db->Execute($qry);
  }

  //write to AstConfig or File
  //writeMIsdn(false);

  //update the misdn-init.conf
  if ($installedPort>0) writeMisdnInitConfFile(true);
  else writeMisdnInitConfFile(false);
  
  if ($res===true) $res=$card_id;
  return $res;
}

// ---- delCard(id:int):int -----------------------------------------------

function delCard($id) {
  global $db;
  $res=true;
  $qry="DELETE FROM cw_isdn_port WHERE card_id=$id";
  $res&=$db->Execute($qry);
  writeMisdnInitConfFile(true);
  return 1;
}

//******************************************************************************
//******************               ISDN PORT               *********************
//******************************************************************************

// ---- getIsdnPort(id:int):Port[] -----------------------------------------------

function getIsdnPort($id) {
  global $db;

  //get the port option id
  $res2=$db->Execute("SELECT id FROM cw_option WHERE category='mISDN' AND name='ports'");
  $arr=$res2->FetchRow();
  $idPortOption=$arr["id"];

  $count=1;
  if ($id!="") $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_isdn_port $condition ORDER BY card_id, port";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {

    //check if available from isdn port group
    $res2=$db->Execute("SELECT * FROM cw_isdn_portgroup WHERE port=".$arr["id"]);
    if ($res2->RecordCount()>0) $arr["available"]=0; else $arr["available"]=1;

    //check if available from isdn extension
    if ($arr["available"]==1) {
      $res2=$db->Execute("SELECT value FROM cw_client c INNER JOIN cw_option_entity oe ON c.id=oe.entity_id WHERE oe.option_id=$idPortOption AND c.technology=".MISDN);
      while ($arrOpt=$res2->FetchRow()) {
        $portValue=$arrOpt["value"];
        error_log($portValue);
        if ($count++==$portValue) $arr["available"]=0; else $arr["available"]=1;
      }
    }
          
    $ret[]=$arr;
  }
  return $ret;
}

// ---- updateIsdnPort(ports:Port[]):int -------------------------------------------

function updateIsdnPort($ports) {
  global $db;
  $res=true;
  foreach ($ports as $p) {
    $id=$p["id"];
    $layermask=$p["layermask"];
    $protocol=$p["protocol"];
    $qry="UPDATE cw_isdn_port SET layermask='$layermask', protocol='$protocol' WHERE id=$id";
    $res&=$db->Execute($qry);
  }
  writeMisdnInitConfFile(true);
  return $res;
}

// ---- writeMisdnInitConfFile():int ----------------------------------------------
//private

function writeMisdnInitConfFile($stop=false) {
  global $db,$voiceoneWSDocumentRoot,$voTools;
  $txt="";
  $ports=getIsdnPort(0);
  $card_old=0;
  $count=1;
  
  foreach ($ports as $p) {
    $card_id=$p["card_id"];
    $card=$p["card"];
    $port=$p["port"];
    $layermask=$p["layermask"]; 
    $protocol=$p["protocol"];
    if ($card_id!=$card_old) {
      if ($card==1) $driver="hfcpci";
      if ($card==2) $driver="0x4";
      if ($card==3) $driver="0x8";
      $cardLine.="card=".$card_id.",".$driver."\n";
    } 
    if ($layermask=="0xf") {
      if ($protocol=="0x2") $te_ptmp.=$count.","; else $te_ptp.=$count.",";
    } else {
      if ($protocol=="0x12") $nt_ptmp.=$count.","; else $nt_ptp.=$count.",";
    }
    
    $card_old=$card_id;
    $count++;

  }  //end of while
  
  $txt.=$cardLine;
  
  if ($te_ptmp!="") $txt.="te_ptmp=".substr($te_ptmp,0,-1)."\n"; 
  if ($te_ptp!="")  $txt.="te_ptp=".substr($te_ptp,0,-1)."\n";
  if ($nt_ptmp!="") $txt.="nt_ptmp=".substr($nt_ptmp,0,-1)."\n"; 
  if ($nt_ptp!="") $txt.="nt_ptp=".substr($nt_ptp,0,-1)."\n";
  
  $txt.="poll=128\n";
  $txt.="debug=0\n";
  $txt.="dsp_options=0\n";
  
  $filename=$voiceoneWSDocumentRoot."/sandbox/misdn-init.conf";
  $ret=file_put_contents($filename,$txt); 

  unset($out);
  //restart the asterisk
  if ($stop===true) {
    error_log("sudo $voTools safe_asterisk stop");exec("sudo $voTools safe_asterisk stop",$out);errArr($out);//stop the asterisk
    error_log("sudo $voTools misdn_init stop");exec("sudo $voTools misdn_init stop",$out);errArr($out); //stop the asterisk driver
  }
  error_log("sudo ".$voTools);exec("sudo ".$voTools." misdn_conf \"$voiceoneWSDocumentRoot/sandbox\" ",$out);errArr($out); //move the misdn configuration file
  errArr($out);
  if ($count>1) {
    error_log("sudo $voTools misdn_init restart");exec("sudo $voTools misdn_init restart",$out); errArr($out);
    sleep(5);
    error_log("sudo $voTools safe_asterisk start");exec("sudo $voTools safe_asterisk start",$out); errArr($out); //start the asterisk    
  }      
  error_log("Completed!");
  return $ret;
}

//******************************************************************************
//******************               ISDN GROUP              *********************
//******************************************************************************

// ---- getIsdnGroup(id:int):Group[] -------------------------------------------

function getIsdnGroup($id,$onlyCustomOption=false) {
  global $db;
  $count=0;
  if ($id!="") $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_isdn_group $condition";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    unset($opt);
    $group_id=$arr["id"];
    $name=$arr["name"];
    $msns=$arr["msns"];
    //$channel_order=$arr["Channel_order"];
    $channel=getChannel(getChannelFromEntity($group_id));
    $arr["speeddial"]=$channel[0]["speeddial"];
    $arr["channel_order"]=$channel[0]["order"];
    
    //extract the ports of the group
    unset($port);
    $qry="SELECT * FROM cw_isdn_portgroup WHERE `group`=$group_id";
    $res2=$db->Execute($qry);
    while ($arr2=$res2->FetchRow()) $port[]=$arr2["port"];
    $arr["port"]=$port;

    //extract the phonenumber of the group
    $arr["msns"]=split("\|",$msns);

    //read the specific group option
    $arr["option"]=getEntityOption($id);
    
    $ret[]=$arr;
  }
  return $ret;
}

// ---- putIsdnGroup(group:IsdnGroup):int ----------------------------------------------

function putIsdnGroup($group) {
  global $db,$misdnPrefix;
  
  //del group if already exists
  if (isset($group["id"]) and $group["id"]!="") {

    $old_channel=getChannel(getChannelFromEntity($group["id"]));
    $channel["id"]=$old_channel[0]["id"];
    $channel["order"]=$old_channel[0]["order"];

    delIsdnGroup($group["id"],false);
    $entity["id"]=$group["id"];
  }
  
  $ret=1;
  $name=$group["name"];
  //$channel_order=$group["Channel_order"];//delete??
  $msns=msns2string($group["msns"]);
  $ports=$group["port"];
  $port_type=$group["port_type"];
  $did=$group["did"];
  $rule="0";

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

  //extract the rule set ID
  $rule=extractRuleFromOption($group["option"],MISDN);

  //insert the isdn group
  $qry = "INSERT INTO cw_isdn_group (`id`,`name`,`rule`,`msns`,`port_type`,`did`) VALUES ($entity_id,'$name',$rule,'$msns','$port_type','$did')";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Isdn Group creation error, mysql INSERT command failed'); 

  //save the group specific options 
  putEntityOption($group["option"],MISDN,$entity_id,$group);
  
  //insert the ports
  if (is_array($ports))
  foreach($ports as $p) {
    $qry = "INSERT INTO cw_isdn_portgroup (`port`,`group`) VALUES ($p,$entity_id)";
    $res=$db->Execute($qry);
  }

  //read all the installed port  
  $arrPort=getIsdnPort(0);

  //insert the ports
  $portCount=1;
  $portList="";
  if (@is_array($arrPort))
  foreach ($arrPort as $p) {
    if (@in_array($p["id"],$ports)) $portList.=$portCount.",";
    $portCount++;
  }  
  $portList=substr($portList,0,-1);
  $res=putAstConfig("misdn.conf", "$name", "ports", $portList);
  

  //insert the number (msns)
  $msnsArr=split("\|",$msns);
  if (is_array($msnsArr)) {
    foreach ($msnsArr as $n) {
      //remove the first zero if exists
      if (substr($n,0,1)=="0") $n=substr($n,1); 
      $numberList.=$n.",";
    }
    $numberList=substr($numberList,0,-1);
    if ($numberList=="" or $did==1 ) $numberList="*";
    delAstConfig("misdn.conf","$name","msns"); 
    $res=putAstConfig("misdn.conf", "$name", "msns", $numberList);
  } 
  
  //put the new Channel
  $channel['technology']=MISDN;
  $channel['resource']="g:".$name;
  $channel['key']=$entity_id;
  $channel['msns']=$group["msns"];
  $channel['rule']=$rule;
  $channel['trunk']=1;
  $channel['did']=$did;
  $channel['speeddial']=$group["speeddial"];
  $channelId=putChannel($channel);  

  //update the incoming context
  $res=delAstConfig("misdn.conf", "$name", "context");
  $res=putAstConfig("misdn.conf", "$name", "context", "trunk_".$channelId);
    
  //write the global Variables
  generateGlobalVar();

  //write to AstConfig or File
  //writeMIsdn();
  

  //restart asterisk (workaround until chan_mISDN fixed)
  //error_log("sudo $safe_asterisk stop");exec("sudo $safe_asterisk stop",$out);errArr($out);//stop the asterisk
  //error_log("sudo $safe_asterisk start");exec("sudo $safe_asterisk start",$out);errArr($out);//start the asterisk

  //reload the chan_misdn
  sendCommand("unload chan_misdn.so");
  //error_log($ret);
  sleep(2);

  sendCommand("load chan_misdn.so");
  //error_log($ret);
  sleep(2);

  sendCommand("reload");
  //error_log($ret);
  sleep(3);
  
  
  if ($ret>0) $ret=$entity_id;
  return $ret;
}

// ---- delIsdnGroup(id:int):int --------------------------------------------------

function delIsdnGroup($id,$reload=true) {
  global $db,$misdnPrefix;
  
  //retrieve the queue name
  $g=getIsdnGroup($id);
  $name=$g[0]["name"];

  //remove the options written in the asterisk table
  delEntityOption($id, MISDN);
  
  $qry="DELETE FROM cw_isdn_group WHERE id='$id'";
  $res=$db->Execute($qry);
  if ($res===false) return new soap_fault('Server','','Isdn group remove error, mysql DELETE command failed');

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

  //update the Channels
  $channelId=getChannelFromEntity($id);
  if ($channelId!=0) delChannel($channelId);

  $ret=delEntity($id);
  if ($reload!==false) {

    //restart asterisk (workaround until chan_mISDN fixed)
    //error_log("sudo $safe_asterisk stop");exec("sudo $safe_asterisk stop",$out);errArr($out);//stop the asterisk
    //error_log("sudo $safe_asterisk start");exec("sudo $safe_asterisk start",$out);errArr($out);//start the asterisk

    //reload the chan_misdn
    $ret=sendCommand("unload chan_misdn.so");
    //error_log($ret);
    sleep(2);
  
    $ret=sendCommand("load chan_misdn.so");
    //error_log($ret);
    sleep(2);
  
    $ret=sendCommand("reload");
    //error_log($ret);
    sleep(3);
  }
  
  return 1;
}



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

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

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

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

function getConfOption($id,$category="") {
  global $db;
  $count=0;
  if ($id!="") $condition="WHERE id=$id ";
  else if ($category!="") $condition="WHERE category='$category' ";
  $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,$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);

  //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)  and 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=string2sql($optionsArr[$option_id]);
    } else {
      $allOptionsArr[$i]["commented"]="1";      
      $commented="1";
    }

    $qry="UPDATE cw_option set value='$value', commented=$commented WHERE id=$option_id";
    $db->Execute($qry); 
  }
  
  //write the config file
  $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;
    delAstConfig($config_file, $context, $o["name"]);  
    if ($o["commented"]==0) putAstConfig($config_file, $context, $o["name"], $value);    
  }
  
  if ($reload==true) sendCommand("reload");

  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"];

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

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

  //cycle between options
  if (is_array($options))
  foreach ($options as $i=>$o) {
    $option_id=$o["key"];
    $value=string2sql($o["value"]);
    //error_log($option_id);
    
    //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"];
    //error_log($arr["name"]." - ".$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) $options[]=array("name"=>"name", "value"=>$context);
  
  //add some custom options
  if (($tech==SIP or $tech==IAX2) and $entityType==EXTENSION) {
    $options[]=array("name"=>"secret",    "value"=>$otherOptions["password"]);
    $options[]=array("name"=>"username",  "value"=>$context);
    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");
  }
  if ($tech==MISDN and $entityType==EXTENSION) {
    $options[]=array("name"=>"msns",  "value"=>$otherOptions["extension"]);
  }
  if ($techVoip==1 and $entityType==PROVIDER) {
    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("insecure",$options))  
      $options[]=array("name"=>"insecure",  "value"=>"very");
  }

  if ($entityType==OFFICE) {
    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) {
      $field_list.=$o["name"].",";
      $value_list.="'".$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)";
    //error_log($qry);
    $db->Execute($qry);
    
  //case NOT REALTIME
  } else {
    $config_file=$techArr[0]["config_file"];
    foreach ($options as $i=>$o) {
      if ($config_file=="zapata.conf")
        putAstConfigZap($config_file, 'channels', $o["name"], $o["value"],'','0',$otherOptions['name']);
      else
        putAstConfig($config_file, $context, $o["name"], $o["value"]);
    }
  }  
  
}


// ---- 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"];
  //errArr($techArr);
  //error_log($entity_id." - ".$tech);
  
  //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 if ($techFile=='zapata.conf') delAstConfigZap($techFile,'channels','','',$context);
  else delAstConfig($techFile, $context);
  
  //error_log("DELETE FROM $techTable WHERE name='$context'");
      
  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=\"$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;
  
  // 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["key"]=0;
  $entity_id=putEntity($entity);
  if (is_object($entity_id) or $entity_id==0) return 0;
  
  //errArr($app);
  
  //insert the application credentials
  $name=string2sql($app["name"]);
  $label=string2sql($app["label"]);
  $description=string2sql($app["description"]);
  $extension=$app["extension"];
  $code=string2sql($app["code"]);
  $macro=$app["macro"];
  $parameter=string2sql($app["parameter"]);
  // here is a bug. $app["group"] contains "\n" instead an array 
  // this causes problems in addEntityToGroup
  //$groups=$app["groups"];
  $groups=array();
  
  $qry="INSERT INTO cw_application (id,name,label,description,extension,code,macro,parameter,groups) ";
  $qry.="VALUES ($entity_id,'$name','$label','$description','$extension','$code','$macro','$parameter','$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;

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

  //reload the extensions
  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"];

  //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    
  $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 the entity
  $ret=delEntity($entity_id);
  
  //reload the extensions
  if ($reload===true) {

    //$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 ('$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, '$name', '$parent', '$order', '$key', '$audio',  '$macro', '$parameter', '$timesegment', '$taudio', '$trepeat', '$iaudio', '$irepeat')";
  $res=$db->Execute($qry);
  //errArr($node);
  //error_log($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);
    //error_log($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);

  //reload the extensions
  sendCommand("reload");

  return 1;
}  

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

function clearIvr($ivr_id) {
  global $db,$ivrPrefix;
  
  $qry="DELETE FROM extensions_table WHERE context like '".$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|s|1)");
      else 
        putAstConfig("extensions.conf", $context, "exten", "$nodeKey,1,Goto($nodeContext|s|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|s|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|s|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)");
  
  
  //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|s|1)");
    putAstConfig("extensions.conf", $context, "exten", "t,1,Goto($context|s|1)");
  }
  
  return;
}

//******************************************************************************
//******************               PLUGIN                   ********************
//******************************************************************************
  
 //---- getPlugin(id:intger):pluginArray ---------------------------
  
/**
 * Get plugin by id from database. If no id is specified, get an array with all plugins
 *
 * @param integer $id: Plugin Id or "" for all plugins
 * @return array $ret array with one or all plugins installed on the system.
 * @see putPlugin
 * @see delPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see getPluginEntities
 */

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

/**
 * Get plugin by name from database.
 *
 * @param string $name: Plugin short name i.e "Wake-Up_Call"
 * @return array $ret array with plugin information or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see putPlugin
 * @see getPluginEntities
 */
function getPluginByName($name) {
  global $db;
  $ret = "";
  $qry = "SELECT * FROM cw_plugin WHERE name = \"$name\" ";
  $res = $db->Execute($qry);
  while($arr = $res->FetchRow()) {
    foreach($arr as $field=>$value) {
      $v[$field] = $value;
    }
    $ret[] = $v;
  }
    
  return $ret;
}


/**
 * Get plugin by packageName (=name.version) from database. 
 * 
 * @param string $name: PluginPackageName (=$name . '.' .$version) i.e "Wake-Up_Call.1.10.0" 
 * @return array $ret array with plugin information or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByEntity
 * @see putPlugin
 * @see delPlugin
 * @see getPluginEntities
 */
function getPluginByPackageName($packageName) {
  global $db;
  $ret = "";
  $qry = "SELECT * FROM cw_plugin WHERE package_name = '$packageName' ";
  $res = $db->Execute($qry);
  while($arr = $res->FetchRow()) {
    foreach($arr as $field=>$value) {
      $v[$field] = $value;
    }
    $ret[] = $v;
  }
    
  return $ret;
}
  
/**
 * Get plugin entities for an activated plugin from database. If no id is specified, get an array with all plugins
 *
 * @param integer $id: Plugin Id or "" for all plugins
 * @return array $ret array with plugin entities or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see putPlugin
 * @see delPlugin 
 */
function getPluginEntities($pluginId) {
  global $db;
  if ($pluginId != "") { $condition=" WHERE plugin_id = '$pluginId'"; }
  $qry = "SELECT * FROM cw_plugin_entity $condition";
  $res = $db->Execute($qry);
  while($arr = $res->FetchRow()) {
    foreach($arr as $field=>$value) {
      $v[$field] = $value;
    }
    $ret[] = $v;
  }
    
  return $ret;
}

 

/**
 * Get plugin by vo-object (entity). If more than one plugin uses that entity, a list of plugins is returned.
 *
 * @param string $type: Entity type as string like "application", "macro" or "queue". String because entities do not have unique id's
 * @param integer $id: Entity Id
 * @return array $ret array with plugin id(s) or "" if no match
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see putPlugin
 * @see delPlugin 
 */
function getPluginbyEntity($eType, $eId) {
  global $db;  
  $qry = "SELECT * FROM cw_plugin_entity WHERE entity_type = '$eType' and entity_id = '$eId'"; 
  $res = $db->Execute($qry);
  while ($arr = $res->FetchRow()) {
    foreach($arr as $field=>$value) {
      if ($field == 'plugin_id') {$ret[] = $value;}     
    }    
  }
    
  return $ret;
}
 
 
// ---- putPlugin(xmlpli:string,xmlpld:string):int ---------------------------

/**
 * Insert plugin from XML String to the system
 * 
 * Fixes automatically references based on id's between vo-objects like application->macro
 * Note: So far, the plugin support is limited to macros, applications and queues
 * @param string $pli XML of plugin information
 * @param string $pld XML of plugin data (plugin objectcs)
 * @return integer 1=ok 0 or less failure
 * @see delPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @todo handle errors, updates and return errorcode. Support more objects. 
 */
function putPlugin($pli, $pld) {
  
  global $db;
  
  // putplugin works in the following steps
  // - validate xml parameter
  // - read plugin data, put vo-objects in database etc.
  // - fix references based on id's between vo-objects. I.e. app uses macro with id=3
  // - update database with plugin info    
     
  //
  // get plugin information.  
  //
  $xmlArr=xml2array($pli);
 //errArr($xmlArr);
   
  // to be valid, root node must be <plugininfo>. otherwise exit
  if ($xmlArr[0]['name'] != 'plugininfo') {
    //error_log("missing valid plugininfo. no valid plugin");
    return 0;
  }
  
   
  // read plugin info into array.
  foreach ($xmlArr[0]["elements"] as $el) {
    $desc[$el["name"]]=$el["text"];
  }
  
   // plugin with the same name must not be activated. otherwise exit
  if ($ret = getPluginByName($desc['name']) != "") {
   //error_log("plugin is with the same name is already activated");
   return 0;
  }
   
  
  
  //
  // get plugin data
  //
  $xmlArr=xml2array($pld);
  //errArr($xmlArr);
  
  // root node must be <plugindata> to be valid
  if ($xmlArr[0]['name'] != 'plugindata') {
    //error_log("missing valid plugindata. no valid plugin");
    return 0;
  }
  
  
  // ok - seems valid. lets walk though the vo-objects and add them to the system
  // however, we need to fix references, because all new objects will get new id's
  // i.e if a plugin imports an app and a macro, then we need to adjust the reference to the macro in the app.
  // for this we use an associative array that maps old and new id's.
    
  // map old and new id's. [type][$importId]=newId
  $idMapper = array();    
  
  // need to loop 2 times. 
  // 1st to get the new id's for all elements if necessary
  // 2nd to fix the references via $idMapper and rebuild the objects
  // if all objects already existed, we break and omnit the 2nd loop. 
  $lStates = array('stInit', 'stFixId');
  foreach ($lStates as $lState) {  
    
    foreach ($xmlArr[0]['elements'] as $item) {
      
      unset($entry);   
      $type = $item["name"];
      switch ($type) {      
        
        // xml element is macro
        // we do not need to fix anything in stFixId
        case "macro":           
          if ($lState == 'stFixId') {  break; }
                    
          foreach ($item["elements"] as $el) {
            $entry[$el["name"]] = $el["text"];
            
            // get the $importId for the idMappper
            if ($el["name"] == "id") { $importId = $el["text"]; }
            
            // get the parameter section
            if ($el["name"] == "parameter") {
              $txt = "";
              foreach ($el["elements"] as $e) {
                $txt .= "<param name=\"" . $e["attributes"]["name"] . "\" type=\"" . $e["attributes"]["type"];
                $txt .= "\" label=\"" . $e["attributes"]["label"] . "\" description=\"" . $e["attributes"]["description"] . "\" />";
              }
              $entry[$el["name"]] = $txt;
            } //if  parameter
            
          }                
          // put macro by name, not by id. 
          $newId = putMacro($entry, false);
          if ($lState == 'stInit') { 
            $entity[$type][] = $newId; 
            //error_log("inserted macro id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }  
          }          
        break;
        
        
        // xml element is application
        // we need to fix the id's of macro references in stFixId
        case "application": 
          foreach ($item["elements"] as $el) {
            $entry[$el["name"]] = $el["text"];
            
            // check the id
            if ($el["name"] == "id") { 
              
              // in stInit store the $importId for the idMapper
              if ($lState == 'stInit') { $importId = $el["text"]; } 
              // fixid: in stFixId use the newId assigned as in stInit
              if ($lState == 'stFixId') {
                if ($idMapper['application'][$el["text"]] != "") { $entry[$el["name"]] = $idMapper['application'][$el['text']]; }                
              }
            }
            
            // fix reference to the macro
            if ($el["name"] == "macro") { 
              if  ($lState == 'stFixId') {          
                if ($idMapper['macro'][$el["text"]] != "") { $entry[$el["name"]] = $idMapper['macro'][$el['text']]; } 
              }
            }
            
          } // foreach 
          // put application by name, not by id. request reload             
          $newId = putApplication($entry, false);
          if ($lState == 'stInit') { 
           $entity[$type][] = $newId;
            //error_log("inserted application id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }  
          }
        break;
        
        
        // xml element is queue 
        // so far we do not support extensions and therefore we do not need to fix anything in stFixId
        // even we would, how would we translate queuemember?       
        case "queue": 
          // if we suport extensions, we might have to change this!
          if ($lState == 'stFixId') {  break; }
          
          foreach ($item["elements"] as $el) {
            $entry[$el["name"]] = $el["text"];
            // get the $importId
            if ($el["name"] == "id") { $importId = $el["text"]; }
            if ($el["name"] == "member") { 
              foreach ($el["elements"] as $e) { $v[] = $e["text"]; }
              $entry['member'] = $v;
            }
          }
          // putQueue by name, not by id
          $newId = putQueue($entry, false);
          if ($lState == 'stInit') { 
            $entity[$type][] = $newId; 
            //error_log("inserted queue id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }  
          }                   
        break;
        
        default:
          //error_log("item not supported");
        break;
        
      }
    } // foreach tag 
  
    // if all objects have existed, than no id's will have changed. so we do not need a 2nd run
    if ((count($idMapper) == 0) and ($lState == 'stInit')) { break; }
  
  } // foreach
 
    
  // 
  // update database with the new plugin information
  //
      
  
  // build information array. map database fields
  $d_name = $desc['name'];
  $d_label = $desc['label'];
  $d_package_name = $desc['packageName'];
  $d_version = $desc['version'];
  $d_date = $desc['date']; 
  $d_description = $desc['description'];
  $d_author_name = $desc['authorName'];
  $d_author_email = $desc['authorEmail'];
  $d_author_website = $desc['authorWebsite'];
  $d_vo_version = $desc['voVersion'];
  $d_vo_pl_version = $desc['voPlVersion'];

  $qry = "INSERT INTO cw_plugin ";
  $qry .= " (name, label, package_name, version, date, description, author_name, author_email, author_website, vo_version, vo_pl_version) ";
  $qry .= " VALUES ";
  $qry .= " ('$d_name', '$d_label', '$d_package_name', '$d_version', '$d_date', '$d_description', '$d_author_name', '$d_author_email', '$d_author_website', '$d_vo_version', '$d_vo_pl_version')";
   
  //error_log($qry);
  $res=$db->Execute($qry);
  // get new plugin id
  $plugin_id = $db->Insert_ID();
  
  // update cw_plugin_entity
  foreach ($entity as $etype=>$elist) {
    foreach ($elist as $elId) {   
   
    $d_plugin_id = $plugin_id;
    $d_entity_id = $elId;
    // we store the entity type verbose like "macro" or "application".
    // this is because we do not have unique id's for all entity types.
    // might change in future version
    $d_entity_type = $etype;
    // not supported so far because entities do not maintain a version
    $d_entity_version = "";
       
    $qry = "INSERT INTO cw_plugin_entity ";
    $qry .= " (plugin_id, entity_type, entity_id, entity_version) ";
    $qry .= " VALUES ";
    $qry .= " ('$d_plugin_id', '$d_entity_type', '$d_entity_id', '$d_entity_version') ";
     
    $res=$db->Execute($qry); 
    }
  }
  
  return 1;
}

// ---- delPlugin(id:int):int ---------------------------

/**
 * Delete a plugin from the system by $id. Removes all entities (vo-objects) that are part of the plugin in case they are not used by other plugins
 * 
 * @param integer $id of plugin to delete
 * @return integer 1
 * @see putPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see getPluginEntities
 * @todo handle errors, return errorcodes, pass info about vo-objects back  (removed/kept)
 */
 function delPlugin($id) {
  global $db;
  // remove vo-objects (entities) in case they are not used by other plugins
  $entities = getPluginEntities($id);
  foreach ($entities as $entity) {
    $type = $entity['entity_type'];
    $plList = getPluginbyEntity($entity['entity_type'], $entity['entity_id']);
    if (is_array($plList) and (count($plList) == 1)) {
      // entity is only used by one plugin, so we remove it
      switch ($type) {
        case "application":
          $ret = delApplication($entity['entity_id'], true);
        break;
        case "macro":
          $ret = delMacro($entity['entity_id'], true);
        break;
        case "queue":
          $ret = delQueue($entity['entity_id']);
        break;      
      } // switch
    } // if
  } // foreach 
  
  
  // delete plugin describtion

  $qry="DELETE FROM cw_plugin WHERE id=$id";
  $ret = $db->Execute($qry);
  // delete plugin objects
  $qry="DELETE FROM cw_plugin_entity WHERE plugin_id=$id";
  $ret = $db->Execute($qry);
  
  
  return 1;
}

//******************************************************************************
//******************               extensions.conf             *****************
//******************************************************************************
  
  
// ---- writeExtensionsConf():int ---------------------------

function writeExtensionsConf($reload=true) {
  global $db,$misdnPrefix;

  //remove all rows of sip.conf. It will be regenerated
  $res=delAstConfig("extensions.conf","%");

  //insert the GENERAL OPTION  
  $arr=getConfOption("",OPTION_EXTENSION_GENERAL);
  if (is_array($arr))
  foreach ($arr as $v) {
    if ($v["value"]=="") $value=$v["default"]; else $value=$v["value"];
    if ($v["commented"]!="1") $res=putAstConfig("extensions.conf", "general", $v["name"], $value);
  }  
    
  //insert the GLOBALS OPTION  
  $arr=getConfOption("",OPTION_EXTENSION_GLOBALS);
  if (is_array($arr))
  foreach ($arr as $v) {
    if ($v["value"]=="") $value=$v["default"]; else $value=$v["value"];
    if ($v["commented"]!="1") $res=putAstConfig("extensions.conf", "globals", $v["name"], $value);
  }  
    
  if ($reload!==false) $ret=sendCommand("sip reload");
    
  return $ret;
}
  
  
//******************************************************************************
//******************               SIP                      ********************
//******************************************************************************
  
  
// ---- writeSIP():int ---------------------------

function writeSIP($reload=true) {
  global $db,$misdnPrefix;

  //remove all rows of sip.conf. It will be regenerated
  $res=delAstConfig("sip.conf","%");

  //insert the GENERAL OPTION  
  $arr=getConfOption("",OPTION_SIP);
  if (is_array($arr))
  foreach ($arr as $v) {
    if ($v["value"]=="") $value=$v["default"]; else $value=$v["value"];
    if ($v["commented"]!="1") $res=putAstConfig("sip.conf", "general", $v["name"], $value);
  }  
    
  // register the SIP Provider
  $res=getProvider(0);
  delAstConfig("sip.conf","general","register"); 
  if (is_array($res))
  foreach ($res as $k=>$v) {
    $value=$v["peer"]["username"].":".$v["peer"]["secret"]."@".$v["peer"]["name"];
    $res=putAstConfig("sip.conf", "general", "register", $value);
  }

  //sip.conf need a "default" context in the ast_config
  $res=putAstConfig("extensions.conf", "default", "exten", "s,1,Hangup");

  if ($reload!==false) $ret=sendCommand("sip reload");
    
  return $ret;
}
  
//******************************************************************************
//******************               IAX                      ********************
//******************************************************************************
  
  
// ---- writeIAX():int ---------------------------

function writeIAX($reload=true) {
  global $db,$misdnPrefix;

  //remove all rows of sip.conf. It will be regenerated
  $res=delAstConfig("iax.conf","%");

  //insert the GENERAL OPTION  
  $arr=getConfOption("",OPTION_IAX); 
  if (is_array($arr))
  foreach ($arr as $v) {
    if ($v["value"]=="") $value=$v["default"]; else $value=$v["value"];
    if ($v["commented"]!="1") $res=putAstConfig("iax.conf", "general", $v["name"], $value);
  }  
    
  // register the IAX Provider
  //$res=getProvider(0,IAX_PROVIDER);
  //delAstConfig("iax.conf","general","register"); 
  //if (is_array($res))
  //foreach ($res as $k=>$v) {
  //  $value=$v["peer"]["username"].":".$v["peer"]["secret"]."@".$v["peer"]["host"];
  //  if ($v["peer"]["port"]!="") $value.=":".$v["peer"]["port"];
  //  $res=putAstConfig("iax.conf", "general", "register", $value);
  //}

  if ($reload!==false) $ret=sendCommand("iax2 reload");
    
  return $ret;
}
  
  

//******************************************************************************
//******************               INITIALIZE               ********************
//******************************************************************************

// ---- initialize():int ----------------------------------------------------

function initialize($genKeys) {
  global $db,$synDbUser,$synDbPassword,$voiceoneWSDocumentRoot, $voiceoneWSDirName;
  global $keyName,$sandboxServer,$voTools,$synDbName, $outgoing_prefix;

  //stop Asterisk
  exec("sudo $voTools safe_asterisk stop",$out);
  errArr($out);
  
  //stop mISDN driver
  exec("sudo $voTools misdn_init stop",$out);
  errArr($out);
  
  //remove all the tables 
  //TODO: remove only voiceone tables
  $tables=$db->MetaTables('TABLES');
  if (is_array($tables))
  foreach ($tables as $t) $db->Execute("DROP TABLE `$t`");

  //write the right file permission
  $doc_root=substr($voiceoneWSDocumentRoot,0,-strlen($voiceoneWSDirName)-1);
  exec("sudo $voTools install ".$doc_root,$out);
  error_log("sudo $voTools install ".$doc_root );

  //insert the database structures
  exec("cat ".$voiceoneWSDocumentRoot."/config/backup/structure.sql | mysql --user=$synDbUser --password=$synDbPassword $synDbName",$out);

  //write the mISDN configuration 
  writeExtensionsConf(false);  

  //create the metagroups
  putGroup(Array("id"=>GROUP_OF_ALL,          "name"=>"_all_",              "type"=>GROUP_OF_ALL,         "metagroup"=>1));
  putGroup(Array("id"=>GROUP_OF_EXTENSION,    "name"=>"_all-extensions_",   "type"=>GROUP_OF_EXTENSION,   "metagroup"=>1));
  putGroup(Array("id"=>GROUP_OF_RESOURCE,     "name"=>"_all-resources_",    "type"=>GROUP_OF_RESOURCE,    "metagroup"=>1));
  putGroup(Array("id"=>GROUP_OF_OFFICE,       "name"=>"_all-officies_",     "type"=>GROUP_OF_OFFICE,      "metagroup"=>1));
  putGroup(Array("id"=>GROUP_OF_APPLICATION,  "name"=>"_all-applications_", "type"=>GROUP_OF_APPLICATION, "metagroup"=>1));
  putGroup(Array("id"=>GROUP_OF_PHONENUMBER,  "name"=>"_all-phonenumbers_", "type"=>GROUP_OF_PHONENUMBER, "metagroup"=>1));
  
  //create the MetaGroup inclusions
  $res=putAstConfig("extensions.conf","_all_","include","_all-extensions_");
  //$res=putAstConfig("extensions.conf","_all_","include","_all-officies_");
  $res=putAstConfig("extensions.conf","_all_","include","_all-resources_");
  $res=putAstConfig("extensions.conf","_all_","include","_all-applications_");
  $res=putAstConfig("extensions.conf","_all_","include","_all-phonenumbers_");
  $res=putAstConfig("extensions.conf","_all_","include","_catch-all_");
  if ($outgoing_prefix=="") {
    $res=putAstConfig("extensions.conf","_catch-all_","exten","_X.,1,NoOp(Catch-All triggered. CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
    $res=putAstConfig("extensions.conf","_catch-all_","exten","_X.,n,AGI(dial.php|entity=&group=".GROUP_OF_PHONENUMBER."&extension=\${EXTEN})");
    $res=putAstConfig("extensions.conf","_catch-all_","exten","_X,1,NoOp(Catch-All triggered. CallerID:\${CALLERID(all)} - Calling:\${EXTEN})");
    $res=putAstConfig("extensions.conf","_catch-all_","exten","_X,n,AGI(dial.php|entity=&group=".GROUP_OF_PHONENUMBER."&extension=\${EXTEN})");
  }  else { 
    $res=putAstConfig("extensions.conf","_catch-all_","exten","_".$outgoing_prefix.".,1,AGI(dial.php|entity=&group=".GROUP_OF_PHONENUMBER."&extension=\${EXTEN})");
  }

  // Load System Plugin
  $pli = file_get_contents($voiceoneWSDocumentRoot."/config/backup/plugininfo.xml"); 
  $pld = file_get_contents($voiceoneWSDocumentRoot."/config/backup/plugindata.xml"); 
  $ret = putPlugin($pli, $pld);

  //Create the first Outgoing Rule
  $arr=Array("name"=>"DefaultOutgoingRule","type"=>RULE_OUTGOING);
  $rule=putRule($arr);
  $arr=Array ("rule"=>$rule,"timesegment"=>0,"action"=>1,"resource"=>0,"group"=>GROUP_OF_ALL);
  $ret=putOutgoingRule($arr);
  
  //Create the first Incoming Rule
  $arr=Array("name"=>"DefaultIncomingRule","type"=>RULE_INCOMING);
  $rule=putRule($arr);
  $arr=Array ("rule"=>$rule,"timesegment"=>0,"macro"=>5,"parameter"=>"dest=2");
  $ret=putIncomingRule($arr);
  
  //write the mISDN configuration 
  //writeMIsdn(false);  

  //write the SIP configuration 
  //writeSIP(false);  

  //write the SIP configuration 
  //writeIAX(false);
  
  //write conf settings
  $techArr=getTechnology(null);
  foreach ($techArr as $t) {
    $ret=putOption($t["id"],null,false);
  }    
 
  //generate the global variable
  generateGlobalVar();

  //Generate the private and public key and move it in the right position
  if ($genKeys==1) {
    exec("sudo $voTools astgenkey \"$voiceoneWSDocumentRoot/sandbox/$keyName\" ",$output,$retScript);
    exec("sudo $voTools inkey \"$voiceoneWSDocumentRoot/sandbox/$keyName\" ",$output,$retScript);
  }

  //error_log("Setup Completed");
  //the asterisk is now DOWN. It will be started when the first isdn card is 
  //installed.

  return $ret;
}

  
  

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

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

function getDirectory() {
  global $db;

  
  $start=false;
  $peers=sendCommand("sip show peers");
  if ($peers!="") {
    $lines=split("\n",$peers);
    foreach ($lines as $l) {
      if (strpos($l,"online")!==false) $start=false;
      if ($start) {
        $infos=split("[ ]+",$l);
        $userArr=split("/",$infos[0]);
        $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=split("\n",$peers);
    foreach ($lines as $l) {
      if (strpos($l,"online")!==false) $start=false;
      if ($start) {
        $infos=split("[ ]+",$l);
        $userArr=split("/",$infos[0]);
        $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) {
    $name=$v["name"]." ".$v["surname"]." <".$v["extension"].">";
    $username=$v["extension"];
    if (isset($peerInfo[$username])) $status=$peerInfo[$username];
    else $status="UNKNOWN";
    $arr=array("name"=>"$name", "username"=>"$username", "status"=>"$status");
    $ret[]=$arr;
  }
  return $ret;
}


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

// ---- getUser(id:int):user[] -----------------------------------

function getUser($id) {
  global $db;

  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"];
    if ($entity_id!=0) {
      $client=getClient($entity_id);
      $c=$client[0];
      $fullname=$c["name"]." ".$c["surname"];
      $username=$c["extension"];
      $password=$c["password"];
      $exten=$c["extension"];
      $technology=$c["technology"];
      $entity_id=$entity_id;
    } 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);
  }
  return $ret;
}

// ---- putUser(user:UserArray):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;
  
  //insert the client credentials
  $qry="INSERT INTO cw_user ($id username,password,lang,type,entity_id) ";
  $qry.="VALUES ($id_value \"{$user['username']}\",\"{$user['password']}\",\"{$user['lang']}\",\"{$user['type']}\",\"{$user['entity_id']}\")";
  $res=$db->Execute($qry);
  if ($res===false) $ret=0; 
  elseif ($user["id"]=="") $ret=$db->Insert_ID();
  else $ret=$user["id"];

  return $ret;
}

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

function delUser($id) {
  global $db;

  //remove the user
  $qry="DELETE FROM cw_user WHERE id=".$id;
  $db->Execute($qry);
  
  return 1;
}

// ---- checkUser(username:string, password:string):int --------------------------------------------------
// PS: password must be an md5 password

function checkUser($username, $password) {
  global $db;
  //error_log("sono in chk user");
  $ret=0;
  $u=getUser(0);
  if (is_array($u))
  foreach($u as $user) {
    if ($user["username"]==$username AND md5($user["password"])==$password) $ret=$user["id"];
  }
  return $ret;
}

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

  // Zapata file
  if ($f=='zapata.conf') return viewAstConfigZap($f);

  // Other files
  $qry="SELECT DISTINCT filename FROM ast_config WHERE filename='$f'";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $filename=$arr["filename"];

    $qry="SELECT DISTINCT category FROM ast_config WHERE filename='$filename' ORDER BY cat_metric";
    $res2=$db->Execute($qry);
    while ($arr=$res2->FetchRow()) {
      $category=$arr["category"];
      $ret.= "\n";
      $ret.= "[".$category."]\n";
      
      $qry="SELECT * FROM ast_config WHERE filename='$filename' AND category='$category' 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) $ret.= "$var_name = $var_val";
        if ($var_name!="" and $commented==1) $ret.= "; $var_name = $var_val";
        if ($comment!="" and $var_name!="") $ret.= "\t ;$comment";
        if ($comment!="" and $var_name=="") $ret.= ";$comment";
        $ret.="\n";
      }
    }
  }
  //error_log($ret);
  return $ret;
}

// ---- createAstConfig(lines:stringArray, filename:string):int ----------------

function createAstConfig($lines,$filename) {
  global $db;
  $cat_metric=-1;
  $var_metric=0;

  // remove all the old records about this $filename
  $qry="DELETE FROM ast_config WHERE filename='$filename'";
  $res=$db->Execute($qry);

  // Ciclo attraverso l'array, si visualizzerà il sorgente come html ed i numeri di linea
  foreach($lines as $line_num => $line) {
    $line=trim($line);
    if ($line{0}=="") continue;
    if ($line{0}==";") {
      $comment=str_replace("\"","'",trim(substr(trim($line),1)));
      $var_name="";
      $var_val="";
      $commented=1;
      $var_metric++;
      //echo "<span color='green'>$comment</span><br>";
    } else {
      $commented="0";
      if ($line{0}=="[") {
        $arr_comm=split(";",trim($line));
        $category=substr(trim($arr_comm[0]),1,strlen(trim($arr_comm[0]))-2);
        $comment=trim($arr_comm[1]);
        //echo "<h1>$category</h1>";
        $cat_metric++;
        $var_metric=-1;
        continue;
      } else {
        $arr=split("=|=>",$line);
        $var_name=trim($arr[0]);
        unset($arr[0]);
        $arr_comm=split(";",trim(implode("=", $arr)));
        $var_val=trim($arr_comm[0]);
        $comment=trim($arr_comm[1]);
        $var_metric++;
        //echo "<b>$var_name</b> || <i>$var_val</i><br>";
      }  
    }
    $var_name=str_replace("\"","\\\"",$var_name);
    $var_val=str_replace("\"","\\\"",$var_val); 
          
    $qry="INSERT INTO ast_config (cat_metric,var_metric,commented,filename,category,var_name,var_val,comment)";
    $qry.="VALUES ($cat_metric,$var_metric,$commented,\"$filename\",\"$category\",\"$var_name\",\"$var_val\",\"$comment\")";
    $res=$db->Execute($qry);
    if ($res==false) error_log ("Errore: $qry"); 
    
  }
  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]";  
  //$ret=file_get_contents ("/var/lib/asterisk/key/".$keyName".pub");
  return $ret; 
}

// ---- stats(source:String, type:String, search:String, from:String, to:String, orderBy:String, page:Int ):CdrArray ----------------

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"];

  errArr($request); 
  //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;

  //errArr($ts);
  if (isset($ts["id"]) and $ts["id"]!="") {
    delTimeSegment($ts["id"],false);
  }

  if (is_array($ts))
  foreach($ts as $k=>$v) {
    if ($v!="|" and $v!="") {
      $fieldName.="`".$k."`,";
      $fieldValue.="'".$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;
}


//******************************************************************************
//******************               RULE                 ************************
//******************************************************************************

// ---- getRule(id:int, type:int ):RuleArray ----------------

function extractRuleFromOption($optionArr,$tech) {
  global $db;
  
  //get the tech infos
  $techArr      = getTechnology($tech);  
  $techName     = $techArr[0]["name"];
  
  if (is_array($optionArr)) 
  foreach ($optionArr as $o) $optArr[$o["key"]]=$o["value"];
  
  $qry="SELECT * FROM cw_option o WHERE category='$techName'";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    switch ($tech) {
      default:
        if ($arr["name"]=="context" and @array_key_exists($arr["id"], $optArr) ) $ruleName=$optArr[$arr["id"]];
      break;
    }
  }
  
  if ($ruleName!="") {
    $qry="SELECT * FROM cw_rule WHERE name='$ruleName'";
    $res=$db->Execute($qry);
    if ($res->RecordCount()>0) {
      $arr=$res->FetchRow();
      $ret=$arr["id"];
    } else $ret=0;
  } else $ret=0;
  
  return $ret;
}
// ---- getRule(id:int, type:int ):RuleArray ----------------

function getRule($id,$type="") {
  global $db;

  if ($type!="") $condition=" WHERE type=$type ";
  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_rule $condition";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $v["outgoing"]="";
    $v["incoming"]="";
    if ($arr["type"]==RULE_OUTGOING) {
      $qry="SELECT * FROM cw_rule_outgoing  WHERE rule=".$arr["id"]." ORDER BY priority";
      $r=$db->Execute($qry);
      while ($a=$r->FetchRow()) {
        foreach($a as $field=>$value) $vo[$field]=$value;
        $v["outgoing"][]=$vo;
      }
    } else if ($arr["type"]==RULE_INCOMING) {
      $qry="SELECT * FROM cw_rule_incoming  WHERE rule=".$arr["id"]." ORDER BY priority";
      $r=$db->Execute($qry);
      while ($a=$r->FetchRow()) {
        foreach($a as $field=>$value) $vi[$field]=$value;
        $vi["from_number"]=split("\|",$vi["from_number"]);
        $v["incoming"][]=$vi;
      }
    }
    $ret[]=$v;
  }
  //errArr($ret);
  return $ret;
}

// ---- putRule(rule:Rule ):int ----------------

function putRule($rule) {
  global $db;
  
  //errArr($rule);
  if (isset($rule["id"])) {
    delRule($rule["id"],false);
    $idk="id,";
    $idv=$rule["id"].",";
  } 

  $name=$rule["name"];
  $description=$rule["description"];
  if (is_array($rule["outgoing"])) $type=RULE_OUTGOING;
  else if (is_array($rule["incoming"])) $type=RULE_INCOMING;
  else $type=$rule["type"];
  $qry="INSERT INTO cw_rule ($idk name,type,description) VALUES ($idv '$name',$type,'$description')";
  $res=$db->Execute($qry);

  //return value
  if ($rule["id"]=="") $ret=$db->Insert_Id();
  else $ret=$rule["id"];

  //write the ast_config
  if ($type==RULE_OUTGOING) {
    putAstConfig("extensions.conf", $name, "include", "_all_");
    //putAstConfig("extensions.conf", $name, "exten", "h,1,PlayTones(Congestion)");
    putAstConfig("extensions.conf", $name, "exten", "i,1,PlayTones(Congestion)");
    putAstConfig("extensions.conf", $name, "exten", "t,1,Hangup");
  }
  
  return $ret;
}

// ---- delRule(id:int ):int ----------------

function delRule($id,$delRules=true) {
  global $db;

  //error_log("ci sono in del Rule");
  //get Rule Name
  $res=getRule($id);
  $name=$res[0]["name"];
  
  $qry="DELETE FROM cw_rule WHERE id=$id";
  $res=$db->Execute($qry);

  if ($delRules) {
    $qry="DELETE FROM cw_rule_outgoing WHERE rule=$id";
    $res=$db->Execute($qry);
    //error_log($qry);
    $qry="DELETE FROM cw_rule_incoming WHERE rule=$id";
    $res=$db->Execute($qry);
    //error_log($qry);
  }
  
  //remove the entry from the ast_config
  $res=delAstConfig("extensions.conf",$name,"%");

  return 1;
}


//******************************************************************************
//******************               OUTGOING RULE            ********************
//******************************************************************************

// ---- getOutgoingRule(id:int ):OutgoingRuleArray ----------------

function getOutgoingRule($id) {
  global $db;

  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_rule_outgoing $condition ORDER BY priority ASC";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $ret[]=$v;
  }
  return $ret;
}

// ---- putOutgoingRule(rule:OutgoingRule ):int ----------------

function putOutgoingRule($rule) {
  global $db;
  
  if ($rule["id"]!="") {
    delOutgoingRule($rule["id"]);
    $idk="`id`,";
    $idv=$rule["id"].",";
  } 
  
  //set variables
  $ruleId=intval($rule["rule"]);
  $priority=intval($rule["priority"]);
  $timesegment=intval($rule["timesegment"]);
  $group=intval($rule["group"]);
  $entity=intval($rule["entity"]);
  $resource=$rule["resource"];
  $provider=intval($rule["provider"]);
  $action=intval($rule["action"]);

  //get priority
  if ($priority==0) {
    $qry="SELECT MAX(priority) as max FROM cw_rule_outgoing WHERE rule=$ruleId";
    $res=$db->Execute($qry);
    $max=$res->FetchRow();
    $priority=intval($max["max"])+1;
    //error_log("La priorita da inserire è: $priority");
  }

  //insert the rule  
  $qry= "INSERT INTO cw_rule_outgoing ($idk `rule`,`priority`,`timesegment`,`group`,`entity`,`resource`,`provider`,`action`) ";
  $qry.=" VALUES ($idv $ruleId,$priority,$timesegment,$group,$entity,'$resource',$provider,$action)";
  $res=$db->Execute($qry);


  //return value
  if ($rule["id"]=="") $ret=$db->Insert_Id();
  else $ret=$rule["id"];
  
  return $ret;
}

// ---- delOutgoingRule(id:int ):int ----------------

function delOutgoingRule($id) {
  global $db;

  $qry="DELETE FROM cw_rule_outgoing WHERE id=$id";
  $res=$db->Execute($qry);
  

  return 1;
}
  
// ---- moveRule(id:int, direction:int, type:int ):int ----------------

function moveRule($id,$direction,$type=RULE_OUTGOING) {
  global $db;
  //error_log($id." ** ".$direction);
  if ($type==RULE_OUTGOING) $table="cw_rule_outgoing";
  if ($type==RULE_INCOMING) $table="cw_rule_incoming";

  //get the rule set
  $qry="SELECT * FROM $table WHERE id=$id";
  $res=$db->Execute($qry);
  $arr=$res->FetchRow();
  $ruleSet=$arr["rule"];

  $repeat=true;
  $qry="SELECT * FROM $table WHERE rule=$ruleSet ORDER BY priority";
  $res=$db->Execute($qry);
  while ($repeat) {
    $arr=$res->FetchRow();

    $nextId=$currentId;
    $currentId=$previousId;
    $previousId=$arr["id"];

    $nextPr=$currentPr;
    $currentPr=$previousPr;
    $previousPr=$arr["priority"];

    //error_log("$previousId - $currentId - $nextId");
    //error_log("$previousPr - $currentPr - $nextPr");

    if ($currentId==$id) {
      if ($direction=="1") {
        $db->Execute("UPDATE $table set priority=$nextPr    WHERE id=$id");      
        $db->Execute("UPDATE $table set priority=$currentPr WHERE id=$nextId");
        //error_log("UPDATE $table set priority=$nextPr    WHERE id=$id");
        //error_log("UPDATE $table set priority=$currentPr WHERE id=$nextId");
      } else {
        $db->Execute("UPDATE $table set priority=$previousPr WHERE id=$id");      
        $db->Execute("UPDATE $table set priority=$currentPr  WHERE id=$previousId");
        //error_log("UPDATE $table set priority=$previousPr WHERE id=$id");
        //error_log("UPDATE $table set priority=$currentPr  WHERE id=$previousId");
      }      
    }
    if ($arr===false) $repeat=false; 
  }

  return 1;
}

//******************************************************************************
//******************               INCOMING RULE            ********************
//******************************************************************************

// ---- getIncomingRule(id:int ):IncomingRuleArray ----------------

function getIncomingRule($id) {
  global $db;


  if ($id!="") $condition=" WHERE id=$id ";
  $qry="SELECT * FROM cw_rule_incoming $condition ORDER BY priority ASC";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {

    foreach($arr as $field=>$value) $v[$field]=$value;
    $v["from_number"]=split("\|",$v["from_number"]);
    $ret[]=$v;
  }
  return $ret;
}

// ---- putIncomingRule(rule:IncomingRule ):int ----------------

function putIncomingRule($rule) {
  global $db;
  
  if ($rule["id"]!="") {
    delIncomingRule($rule["id"]);
    $idk="`id`,";
    $idv=$rule["id"].",";
  } 

  //set variables
  $ruleId=intval($rule["rule"]);
  $priority=intval($rule["priority"]);
  $timesegment=intval($rule["timesegment"]);
  $callerid=$rule["callerid"];
  $from_channel=intval($rule["from_channel"]);
  if ($rule["from_number"]!="") $from_number=implode("|",$rule["from_number"]);
  $macro=intval($rule["macro"]);
  $parameter=$rule["parameter"];
  $duration=intval($rule["duration"]);

  //get priority
  if ($priority==0) {
    $qry="SELECT MAX(priority) as max FROM cw_rule_incoming WHERE rule=$ruleId";
    $res=$db->Execute($qry);
    $max=$res->FetchRow();
    $priority=intval($max["max"])+1;
    //error_log("La priorita da inserire è: $priority");
  }

  //insert the rule  
  $qry= "INSERT INTO cw_rule_incoming ($idk `rule`,`priority`,`timesegment`,`callerid`,`from_channel`,`from_number`,`macro`,`parameter`,`duration`) ";
  $qry.=" VALUES ($idv $ruleId,$priority,$timesegment,'$callerid',$from_channel,'$from_number',$macro,'$parameter',$duration)";
  $res=$db->Execute($qry);


  //return value
  if ($rule["id"]=="") $ret=$db->Insert_Id();
  else $ret=$rule["id"];
  
  return $ret;
}

// ---- delIncomingRule(id:int ):int ----------------

function delIncomingRule($id) {
  global $db;

  $qry="DELETE FROM cw_rule_incoming WHERE id=$id";
  $res=$db->Execute($qry);

  return 1;
}
  
// ---- moveIncomingRule(id:int, direction:int ):int ----------------

function moveIncomingRule($id,$direction) {
  global $db;
  //error_log($id." ** ".$direction);
  $repeat=true;
  $qry="SELECT * FROM cw_rule_outgoing ORDER BY priority";
  $res=$db->Execute($qry);
  while ($repeat) {
    $arr=$res->FetchRow();

    $nextId=$currentId;
    $currentId=$previousId;
    $previousId=$arr["id"];

    $nextPr=$currentPr;
    $currentPr=$previousPr;
    $previousPr=$arr["priority"];

    //error_log("$previousId - $currentId - $nextId");
    //error_log("$previousPr - $currentPr - $nextPr");

    if ($currentId==$id) {
      if ($direction=="1") {
        $db->Execute("UPDATE cw_rule_outgoing set priority=$nextPr    WHERE id=$id");      
        $db->Execute("UPDATE cw_rule_outgoing set priority=$currentPr WHERE id=$nextId");
        //error_log("UPDATE cw_rule_outgoing set priority=$nextPr    WHERE id=$id");
        //error_log("UPDATE cw_rule_outgoing set priority=$currentPr WHERE id=$nextId");
      } else {
        $db->Execute("UPDATE cw_rule_outgoing set priority=$previousPr WHERE id=$id");      
        $db->Execute("UPDATE cw_rule_outgoing set priority=$currentPr  WHERE id=$previousId");
        //error_log("UPDATE cw_rule_outgoing set priority=$previousPr WHERE id=$id");
        //error_log("UPDATE cw_rule_outgoing set priority=$currentPr  WHERE id=$previousId");
      }      
    }
    if ($arr===false) $repeat=false; 
  }

  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
 */

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;
    $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
 */
function getMacroByName($name) {
  global $db;

  //get macro by name  
  $qry="SELECT * FROM cw_macro WHERE name=\"$name\" ORDER BY label";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    foreach($arr as $field=>$value) $v[$field]=$value;
    $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 
 */
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["key"]="0";
  $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=str_replace("'","\\'",$macro["parameter"]);
  $code=str_replace("'","\\'",$macro["code"]);
  $qry="INSERT INTO cw_macro (id,name,label,description,code,parameter) ";
  $qry.="VALUES ($entity_id,'$name','$label','$description','$code','$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=split("\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;
  }  
  $arrCode=array_merge(Array("[macro-$name]"),split("\n",$macro["code"]));

  //$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 { 
      $astconfig=array_merge($arrCode, $astconfig_after);
    }
  }
  // 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;

  //reload the extensions
  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
 */

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-$name'";
  $db->Execute($qry);
  
  //remove the entity from the asterisk tables
  $ret=delEntity($id); 
  
  //reload asterisk configuration
  if ($reload===true)  sendCommand("reload");
  
  return 1;
}


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

//******************************************************************************
//******************               AUDIO                    ********************
//******************************************************************************

/**
 * Get list of audiofiles
 *
 * @return array audiofile[] using the database record structure
 */
function getAudioFile() {
  global $db;
  $qry="SELECT * FROM cw_audio";
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) {
    $ret["name"]=$arr["name"]; 
    $ret["filename"]=$arr["filename"]; 
    $ret["date"]=$arr["date"]; 
    $ret["id"]=$arr["id"]; 
    $result[]=$ret;
  }
  return $result;
}

function putAudioFile($name,$filename) {
  global $db;
  $date=date("Y-m-d H:s:i");
  $qry="SELECT * FROM cw_audio WHERE filename='$filename'";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    $qry="UPDATE cw_audio SET `name`='$name', `date`='$date' WHERE `filename`='$filename'";
    $db->Execute($qry);
  } else {
    $qry="INSERT INTO cw_audio (`name`,`filename`,`date`) VALUES ('$name','$filename','$date')";
    $db->Execute($qry);
  }
  return 1;
}


function recordAudioFile($filename,$clientId) {
  global $floapi,$API_user,$API_pass,$API_host;

  $AUDIO_RECORDER_APPLICATION=30;
  
  //retrieve some information from db
  $clientArr=getClient($clientId,0);
  $client=$clientArr[0];
  $channelArr=getChannel(getChannelFromEntity($clientId));
  $channelName=$channelArr[0]["name"];
  $ruleId=$client["rule"];
  $ruleArr=getRule($ruleId);
  $ruleName=$ruleArr[0]["name"];
  $applicationArr=getApplication($AUDIO_RECORDER_APPLICATION);
  $application=$applicationArr[0];
  $appExtension=$application["extension"];
  
  //originate a call
  $entry=array('Channel'=>$channelName,'Exten'=>$appExtension,'Context'=>$ruleName,'Priority'=>1,'Callerid'=>"Audio Recorder <$filename>");
  $floapi = new floAPI($API_user, $API_pass, $API_host);
  if ($floapi->socket!="") {
    $ret= $floapi->request("Originate", $entry);
    $floapi->close();
    return 1;
  } else return 0; 
}


/**
* Delete an audiofile from the database and from the filesystem. Works only in the IVR file directory
*
* @param string $filename
* @return integer 1 = ok, 0 = error (file not found or file in use)
* @todo Should inform the  user that if a  request for delete an audiofile  would break one of the IVR's and therefore nothing is deleted.
*/
function deleteAudioFile($filename) {
  global $db, $ivrStartingDir, $voTools;
  $qry="SELECT * FROM cw_audio WHERE filename='$filename'";
  $res=$db->Execute($qry);
  if ($res->RecordCount() == 0) {
    // audiofile does not exist in database. that should never happen.
	return 0;
  }
  if (isUsedIVRAudioFile($filename)) {
      // audiofile is in use by an IVR - we do not delete anything
      // TODO: Should inform the  user that this would break one of the IVR's and nothing is deleted.
	  error_log("deleteAudioFile: Audiofile:$filename is in use, so it will not be deleted");
	  return 0;
  }
  
  // all ok.
  // delete from database
  $qry="DELETE FROM cw_audio WHERE filename='$filename';";
  $res=$db->Execute($qry);
  // delete from filesystem. php function unlink requires root rights so we do a systemcall to run a script
  $mycmd="deleteAudiofile"; $myparam1=$ivrStartingDir.$filename; 
  system("sudo $voTools $mycmd $myparam1");
  error_log("sudo $voTools $mycmd $myparam1");
  return 1;
}


/**
* Rename an audiofile in the database and in the filesystem. Works only in the IVR file directory
* If the filename is in use by any IVR, the IVR Menu is modified to match the new audiofilename
* The user has to rebuild the IVR as if he/she would do for any other change
*
* @param string $filename Existing filename
* @param string $newFilename New filename provided by user
* @param string $newName New name for that file as it shows up in frontend. So far it is the same as NewFilename without extension
* @return integer 1 = ok, 0 = error (filename not found or newFilename in use)
* @todo Inform User if an IVR was modified, which IVR has to be rebuiled
*/
function renameAudioFile($filename, $newFilename, $newName) {
  global $db, $ivrStartingDir, $voTools;
  // check wether current filename exists in database and filesystem
  $qry="SELECT * FROM cw_audio WHERE filename='$filename'";
  $res=$db->Execute($qry);
  if ($res->RecordCount()==0) {
    // filename does not exist. we exit. should not happen!
	return 0;
  }
  // check wether the newFilename already exists in database
  $qry="SELECT * FROM cw_audio WHERE filename='$newFilename'";
  $res=$db->Execute($qry);
  if ($res->RecordCount()>0) {
    // newFilename exists in database. we exit 
	return 0;
  }
 
  // all ok - we can proceed	
  $date=date("Y-m-d H:s:i");
  // rename in database
  $qry="UPDATE cw_audio SET `name`='$newName', `filename`='$newFilename', `date`='$date' WHERE `filename`='$filename'";
  $res=$db->Execute($qry);
  // rename in filesystem. php function rename requires root rights so we do a systemcall to run a script
  $mycmd="renameAudiofile"; $myparam1=$ivrStartingDir.$filename; $myparam2=$ivrStartingDir.$newFilename;
  system("sudo $voTools $mycmd $myparam1 $myparam2");
  error_log("sudo $voTools $mycmd $myparam1 $myparam2");
  
  // see wether the filename was used in any IVR
  if (isUsedIVRAudioFile($filename)) {
    // audiofile is in use - we need to update IVR menus. User needs to rebuild IVR database as usual afterwards
   $qry="UPDATE cw_ivr_node SET `audio`='$newFilename' WHERE `audio`='$filename'";
   $res=$db->Execute($qry);
   $qry="UPDATE cw_ivr_node SET `iaudio`='$newFilename' WHERE `iaudio`='$filename'";
   $res=$db->Execute($qry);
   $qry="UPDATE cw_ivr_node SET `taudio`='$newFilename' WHERE `taudio`='$filename'";
   $res=$db->Execute($qry);
  }

  return 1;
}

//******************************************************************************
//******************               I/O FUNCTION             ********************
//******************************************************************************


/**
* Reboot or shutdown the system
*
* @param string $action
* @return integer 1 = ok, 0 = error 
*/
function shutdown($action) {
  global $voiceoneWSDocumentRoot;
  $voTools=$voiceoneWSDocumentRoot."/config/script/vo-tools.sh";
  if ($action=="reboot") $param="-r now";
  else if ($action=="halt") $param="-h now";
  else return 0;
  system("sudo $voTools shutdown $param");
  error_log("sudo $voTools shutdown $param");
  // FIXME: log info
  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=split("\|",$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;
}


//convert an array of msns in a pipe separated string
//expand range of numbers in a list of numbers
function msns2string($msns) {
  if (is_array($msns)) 
  foreach($msns as $m) {
    if ($m=="") continue;
    if (strpos($m,"-")!==false) {
      $arr=split("-",$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;
}


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,$groupPrefix, $outgoing_prefix;

  //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);

  //OUTGOING PREFIX
  $app="OUTGOING_PREFIX";
  $appdata=$outgoing_prefix;
  $resAstConfig=delAstConfig("extensions.conf","globals",$app);
  $resAstConfig=putAstConfig("extensions.conf","globals",$app,$appdata);

}

// ---- putAstConfig($file:string, $context:string, $var:string, $var_data:string): int 

function putAstConfig($file,$context,$var,$var_data,$comment="",$commented=0) {
  global $db;

  if ($file=="extensions.conf" and $asterikRealtime===true and $var_data!="Realtime/@extensions") {
    //**************************************************************************
    //                              ASTERISK REAL TIME
    //**************************************************************************

    //insert the realtime directive
    putAstConfig("extensions.conf", $context, "switch", "Realtime/@extensions");
    
    //extract the parameters
    ereg ("^(.*),(.*),(.[^(]*)\((.*)\)$", $var_data, $regs);
    $extension["context"]=$context;
    $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.="'".$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
    //**************************************************************************

    $context=string2sql($context);
    $var=string2sql($var);
    $var_data=string2sql($var_data);
    $comment=string2sql($comment);
    
    //check if any other same astconfig entry is already into the table
    $qry="SELECT * FROM ast_config WHERE filename=\"$file\" AND category=\"$context\" AND var_name=\"$var\" AND var_val=\"$var_data\"";
    $res=$db->Execute($qry);
    if ($res->RecordCount()>0) {
      $arr=$res->FetchRow();
      return $arr["id"];  
    }
  
    //calculate the var_metric
    $qry="SELECT * FROM ast_config WHERE filename='$file' and category='$context'";
    $res=$db->Execute($qry);
    $count=$res->RecordCount();
    $arrCat=$res->FetchRow();
    if ($count>0) {
      $qry="SELECT MAX(var_metric) as maxvar FROM ast_config WHERE filename='$file' and category='$context'";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $var_metric=$arr["maxvar"]+1;
      $cat_metric=$arrCat["cat_metric"];
    } else {
      $qry="SELECT MAX(cat_metric) as maxcat FROM  ast_config WHERE  filename='$file'";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $cat_metric=$arr["maxcat"]+1;    
      $var_metric=0;
    }    
  
    //$var=string2sql($var);
    //$var_data=string2sql($var_data); 
    //$comment=string2sql($comment); 
    
    $qry="INSERT INTO ast_config (cat_metric,var_metric,commented,filename,category,var_name,var_val,comment)";
    $qry.="VALUES($cat_metric,$var_metric,$commented,\"$file\",\"$context\",\"$var\",\"$var_data\",\"$comment\")";
    $res=$db->Execute($qry);
    $astconfig_id=$db->Insert_ID();
    return $astconfig_id;
  }
}

// ---- delAstConfig($file:string, $context:string, $var:string): int 

function delAstConfig($file,$category,$var_name="",$var_val="") {
  global $db;
  $qry="DELETE FROM ast_config WHERE filename=\"$file\" AND category like \"$category\" ";
  if ($var_name!="")  $qry.=" AND var_name  like \"".$var_name."\"";
  if ($var_val!="") $qry.=" AND var_val like \"".$var_val."\"";
  $res=$db->Execute($qry);
  return $res;
}


// ---- sendCommand ($cmd:string):int 
// send a command with the Asterisk API Manager interface
function sendCommand($cmd) {
  global $floapi,$API_user,$API_pass,$API_host;
  
  // Connect to asterisk via API
  // Create the object.

  $floapi = @new floAPI($API_user, $API_pass, $API_host);
  
  if ($floapi->socket!="") {
    // Run a test command (sip show peers).
    $ret= $floapi->request("COMMAND", array("COMMAND" => $cmd));
  
    // Close the connection.
    //if (strpos($cmd,"restart")===false ) $floapi->close();
    $floapi->close();

    return $ret;
  } else return 0; 
}

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


/**
 * 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) {
    //$txt=str_replace("'","\\'",$txt);
    $txt=addslashes($txt);
    return $txt;
  }   
  
  
  //recursive array search for multidimensional array
  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;
  }   
?>