<?php namespace Plivo;use GuzzleHttp\Client;class PlivoError extends \Exception {}class RestAPI{ private $api; private $auth_id; private $auth_token; function __construct($auth_id, $auth_token, $url = "https://api.plivo.com", $version = "v1") { if ((!isset($auth_id)) || (!$auth_id)) { throw new PlivoError("no auth_id"); } if ((!isset($auth_token)) || (!$auth_token)) { throw new PlivoError("no auth_token"); } $url = "https://api.plivo.com"; $version = "v1"; $this->version = $version; $this->api = $url."/".$this->version."/Account/".$auth_id; $this->auth_id = $auth_id; $this->auth_token = $auth_token; } public static function validate_signature($uri, $post_params=array(), $signature, $auth_token) { ksort($post_params); foreach($post_params as $key => $value) { $uri .= "$key$value"; } $generated_signature = base64_encode(hash_hmac("sha1",$uri, $auth_token, true)); return $generated_signature == $signature; } public function request($method = "GET",$path, $vars = array()) { $fp = null; $tmpfile = ""; $encoded = ""; $encoded = json_encode($vars, JSON_FORCE_OBJECT); /* construct full url */ $url = $this->api.rtrim($path, '/').'/'; /* initialize a new curl object */ $curl = curl_init($url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); switch(strtoupper($method)) { case "GET": curl_setopt($curl, CURLOPT_HTTPGET, TRUE); break; case "POST": curl_setopt($curl, CURLOPT_POST, TRUE); curl_setopt($curl, CURLOPT_POSTFIELDS, $encoded); break; case "PUT": /* curl_setopt($curl, CURLOPT_PUT, TRUE); */ curl_setopt($curl, CURLOPT_POSTFIELDS, $encoded); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); file_put_contents($tmpfile = tempnam("/tmp", "put_"), $encoded); curl_setopt($curl, CURLOPT_INFILE, $fp = fopen($tmpfile, 'r')); curl_setopt($curl, CURLOPT_INFILESIZE, filesize($tmpfile)); break; case "DELETE": curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); break; default: break; } curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_FRESH_CONNECT, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); /* send credentials */ curl_setopt($curl, CURLOPT_USERPWD, $pwd = "{$this->auth_id}:{$this->auth_token}"); /* do the request. If FALSE, then an exception occurred */ if(FALSE === ($result = curl_exec($curl))) /* get result code */ $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); /* unlink tmpfiles */ if($fp) fclose($fp); if(strlen($tmpfile)) unlink($tmpfile); return $result; } private function pop($params, $key) { $val = $params[ $key ]; if (!$val) { throw new PlivoError($key." parameter not found"); } unset($params[ $key ]); return $val; } public function get_account($params = array()) { return $this->request('GET', '', $params); } public function modify_account($params = array()) { return $this->request('POST', '', $params); } public function get_subaccounts($params = array()) { return $this->request('GET', '/Subaccount/', $params); } public function create_subaccount($params = array()) { return $this->request('POST', '/Subaccount/', $params); } public function get_subaccount($params = array()) { $subauth_id = $this->pop($params, "subauth_id"); return $this->request('GET', '/Subaccount/'.$subauth_id.'/', $params); } public function modify_subaccount($params = array()) { $subauth_id = $this->pop($params, "subauth_id"); return $this->request('POST', '/Subaccount/'.$subauth_id.'/', $params); } public function delete_subaccount($params = array()) { $subauth_id = $this->pop($params, "subauth_id"); return $this->request('DELETE', '/Subaccount/'.$subauth_id.'/', $params); } public function get_applications($params = array()) { return $this->request('GET', '/Application/', $params); } public function create_application($params = array()) { return $this->request('POST', '/Application/', $params); } public function get_application($params = array()) { $app_id = $this->pop($params, "app_id"); return $this->request('GET', '/Application/'.$app_id.'/', $params); } public function modify_application($params = array()) { $app_id = $this->pop($params, "app_id"); return $this->request('POST', '/Application/'.$app_id.'/', $params); } public function delete_application($params = array()) { $app_id = $this->pop($params, "app_id"); return $this->request('DELETE', '/Application/'.$app_id.'/', $params); } public function get_numbers($params = array()) { return $this->request('GET', '/Number/', $params); } public function search_numbers($params = array()) { return $this->request('GET', '/AvailableNumber/', $params); } public function get_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('GET', '/Number/'.$number.'/', $params); } public function modify_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('POST', '/Number/'.$number.'/', $params); } public function rent_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('POST', '/AvailableNumber/'.$number.'/'); } public function unrent_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('DELETE', '/Number/'.$number.'/', $params); } public function search_phone_numbers($params = array()) { return $this->request('GET', '/PhoneNumber/', $params); } public function buy_phone_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('POST', '/PhoneNumber/'.$number.'/'); } public function link_application_number($params = array()) { $number = $this->pop($params, "number"); return $this->request('POST', '/Number/'.$number.'/', $params); } public function unlink_application_number($params = array()) { $number = $this->pop($params, "number"); $params = array("app_id" => ""); return $this->request('POST', '/Number/'.$number.'/', $params); } public function get_number_group($params = array()) { return $this->request('GET', '/AvailableNumberGroup/', $params); } public function get_number_group_details($params = array()) { $group_id = $this->pop($params, "group_id"); return $this->request('GET', '/AvailableNumberGroup/'.$group_id.'/', $params); } public function rent_from_number_group($params = array()) { $group_id = $this->pop($params, "group_id"); return $this->request('POST', '/AvailableNumberGroup/'.$group_id.'/', $params); } public function get_cdrs($params = array()) { return $this->request('GET', '/Call/', $params); } public function get_cdr($params = array()) { $record_id = $this->pop($params, 'record_id'); return $this->request('GET', '/Call/'.$record_id.'/', $params); } public function get_live_calls($params = array()) { $params["status"] = "live"; return $this->request('GET', '/Call/', $params); } public function get_live_call($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); $params["status"] = "live"; return $this->request('GET', '/Call/'.$call_uuid.'/', $params); } public function make_call($params = array()) { return $this->request('POST', '/Call/', $params); } public function hangup_all_calls($params = array()) { return $this->request('DELETE', '/Call/', $params); } public function transfer_call($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('POST', '/Call/'.$call_uuid.'/', $params); } public function hangup_call($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('DELETE', '/Call/'.$call_uuid.'/', $params); } public function record($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('POST', '/Call/'.$call_uuid.'/Record/', $params); } public function stop_record($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('DELETE', '/Call/'.$call_uuid.'/Record/', $params); } public function play($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('POST', '/Call/'.$call_uuid.'/Play/', $params); } public function stop_play($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('DELETE', '/Call/'.$call_uuid.'/Play/', $params); } public function speak($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('POST', '/Call/'.$call_uuid.'/Speak/', $params); } public function stop_speak($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('DELETE', '/Call/'.$call_uuid.'/Speak/', $params); } public function send_digits($params = array()) { $call_uuid = $this->pop($params, 'call_uuid'); return $this->request('POST', '/Call/'.$call_uuid.'/DTMF/', $params); } public function hangup_request($params = array()) { $request_uuid = $this->pop($params, 'request_uuid'); return $this->request('DELETE', '/Request/'.$request_uuid.'/', $params); } public function get_live_conferences($params = array()) { return $this->request('GET', '/Conference/', $params); } public function hangup_all_conferences($params = array()) { return $this->request('DELETE', '/Conference/', $params); } public function get_live_conference($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); return $this->request('GET', '/Conference/'.$conference_name.'/', $params); } public function hangup_conference($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); return $this->request('DELETE', '/Conference/'.$conference_name.'/', $params); } public function hangup_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('DELETE', '/Conference/'.$conference_name.'/Member/'.$member_id.'/', $params); } public function play_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('POST', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Play/', $params); } public function stop_play_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('DELETE', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Play/', $params); } public function speak_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('POST', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Speak/', $params); } public function deaf_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('POST', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Deaf/', $params); } public function undeaf_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('DELETE', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Deaf/', $params); } public function mute_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('POST', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Mute/', $params); } public function unmute_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('DELETE', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Mute/', $params); } public function kick_member($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); $member_id = $this->pop($params, 'member_id'); return $this->request('POST', '/Conference/'.$conference_name.'/Member/'.$member_id.'/Kick/', $params); } public function record_conference($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); return $this->request('POST', '/Conference/'.$conference_name.'/Record/', $params); } public function stop_record_conference($params = array()) { $conference_name = $this->pop($params, 'conference_name'); $conference_name = rawurlencode($conference_name); return $this->request('DELETE', '/Conference/'.$conference_name.'/Record/', $params); } public function get_recordings($params = array()) { return $this->request('GET', '/Recording/', $params); } public function get_recording($params = array()) { $recording_id = $this->pop($params, 'recording_id'); return $this->request('GET', '/Recording/'.$recording_id.'/', $params); } public function delete_recording($params = array()) { $recording_id = $this->pop($params, 'recording_id'); return $this->request('DELETE', '/Recording/'.$recording_id.'/', $params); } public function get_endpoints($params = array()) { return $this->request('GET', '/Endpoint/', $params); } public function create_endpoint($params = array()) { return $this->request('POST', '/Endpoint/', $params); } public function get_endpoint($params = array()) { $endpoint_id = $this->pop($params, 'endpoint_id'); return $this->request('GET', '/Endpoint/'.$endpoint_id.'/', $params); } public function modify_endpoint($params = array()) { $endpoint_id = $this->pop($params, 'endpoint_id'); return $this->request('POST', '/Endpoint/'.$endpoint_id.'/', $params); } public function delete_endpoint($params = array()) { $endpoint_id = $this->pop($params, 'endpoint_id'); return $this->request('DELETE', '/Endpoint/'.$endpoint_id.'/', $params); } public function get_incoming_carriers($params = array()) { return $this->request('GET', '/IncomingCarrier/', $params); } public function create_incoming_carrier($params = array()) { return $this->request('POST', '/IncomingCarrier/', $params); } public function get_incoming_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('GET', '/IncomingCarrier/'.$carrier_id.'/', $params); } public function modify_incoming_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('POST', '/IncomingCarrier/'.$carrier_id.'/', $params); } public function delete_incoming_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('DELETE', '/IncomingCarrier/'.$carrier_id.'/', $params); } public function get_outgoing_carriers($params = array()) { return $this->request('GET', '/OutgoingCarrier/', $params); } public function create_outgoing_carrier($params = array()) { return $this->request('POST', '/OutgoingCarrier/', $params); } public function get_outgoing_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('GET', '/OutgoingCarrier/'.$carrier_id.'/', $params); } public function modify_outgoing_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('POST', '/OutgoingCarrier/'.$carrier_id.'/', $params); } public function delete_outgoing_carrier($params = array()) { $carrier_id = $this->pop($params, 'carrier_id'); return $this->request('DELETE', '/OutgoingCarrier/'.$carrier_id.'/', $params); } public function get_outgoing_carrier_routings($params = array()) { return $this->request('GET', '/OutgoingCarrierRouting/', $params); } public function create_outgoing_carrier_routing($params = array()) { return $this->request('POST', '/OutgoingCarrierRouting/', $params); } public function get_outgoing_carrier_routing($params = array()) { $routing_id = $this->pop($params, 'routing_id'); return $this->request('GET', '/OutgoingCarrierRouting/'.$routing_id.'/', $params); } public function modify_outgoing_carrier_routing($params = array()) { $routing_id = $this->pop($params, 'routing_id'); return $this->request('POST', '/OutgoingCarrierRouting/'.$routing_id.'/', $params); } public function delete_outgoing_carrier_routing($params = array()) { $routing_id = $this->pop($params, 'routing_id'); return $this->request('DELETE', '/OutgoingCarrierRouting/'.$routing_id.'/', $params); } public function pricing($params = array()) { return $this->request('GET', '/Pricing/', $params); } public function send_message($params = array()) { return $this->request('POST', '/Message/', $params); } public function get_messages($params = array()) { return $this->request('GET', '/Message/', $params); } public function get_message($params = array()) { $record_id = $this->pop($params, 'record_id'); return $this->request('GET', '/Message/'.$record_id.'/', $params); }}/* XML */class Element { protected $nestables = array(); protected $valid_attributes = array(); protected $attributes = array(); protected $name; protected $body = null; protected $childs = array(); function __construct($body = '', $attributes = array()) { $this->attributes = $attributes; if ((!$attributes) || ($attributes === null)) { $this->attributes = array(); } $this->name = preg_replace('/^'.__NAMESPACE__.'\\\\/', '', get_class($this)); /* $this->name = get_class($this); */ $this->body = $body; foreach ($this->attributes as $key => $value) { if (!in_array($key, $this->valid_attributes)) { throw new PlivoError("invalid attribute ".$key." for ".$this->name); } $this->attributes[ $key ] = $this->convert_value($value); } } protected function convert_value($v) { if ($v === true) { return "true"; } if ($v === false) { return "false"; } if ($v === null) { return "none"; } if ($v === "get") { return "GET"; } if ($v === "post") { return "POST"; } return $v; } function addSpeak($body = null, $attributes = array()) { return $this->add(new Speak($body, $attributes)); } function addPlay($body = null, $attributes = array()) { return $this->add(new Play($body, $attributes)); } function addDial($body = null, $attributes = array()) { return $this->add(new Dial($body, $attributes)); } function addNumber($body = null, $attributes = array()) { return $this->add(new Number($body, $attributes)); } function addUser($body = null, $attributes = array()) { return $this->add(new User($body, $attributes)); } function addGetDigits($attributes = array()) { return $this->add(new GetDigits($attributes)); } function addRecord($attributes = array()) { return $this->add(new Record($attributes)); } function addHangup($attributes = array()) { return $this->add(new Hangup($attributes)); } function addRedirect($body = null, $attributes = array()) { return $this->add(new Redirect($body, $attributes)); } function addWait($attributes = array()) { return $this->add(new Wait($attributes)); } function addConference($body = null, $attributes = array()) { return $this->add(new Conference($body, $attributes)); } function addPreAnswer($attributes = array()) { return $this->add(new PreAnswer($attributes)); } function addMessage($body = null, $attributes = array()) { return $this->add(new Message($body, $attributes)); } function addDTMF($body = null, $attributes = array()) { return $this->add(new DTMF($body, $attributes)); } public function getName() { return $this->name; } protected function add($element) { if (!in_array($element->getName(), $this->nestables)) { throw new PlivoError($element->getName()." not nestable in ".$this->getName()); } $this->childs[] = $element; return $element; } public function setAttributes($xml) { foreach ($this->attributes as $key => $value) { $xml->addAttribute($key, $value); } } public function asChild($xml) { if ($this->body) { $child_xml = $xml->addChild($this->getName(), htmlspecialchars($this->body)); } else { $child_xml = $xml->addChild($this->getName()); } $this->setAttributes($child_xml); foreach ($this->childs as $child) { $child->asChild($child_xml); } } public function toXML($header = false) { if (!(isset($xmlstr))) { $xmlstr = ''; } if ($this->body) { $xmlstr.= "<".$this->getName().">".htmlspecialchars($this->body)."</".$this->getName().">"; } else { $xmlstr.= "<".$this->getName()."></".$this->getName().">"; } if ($header === true) { $xmlstr = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>".$xmlstr; } $xml = new \SimpleXMLElement($xmlstr); $this->setAttributes($xml); foreach ($this->childs as $child) { $child->asChild($xml); } return $xml->asXML(); } public function __toString() { return $this->toXML(); }}class Response extends Element { protected $nestables = array( 'Speak', 'Play', 'GetDigits', 'Record', 'Dial', 'Redirect', 'Wait', 'Hangup', 'PreAnswer', 'Conference', 'DTMF', 'Message' ); function __construct() { parent::__construct(null); } public function toXML($header = false) { $xml = parent::toXML(true); return $xml; }}class Speak extends Element { protected $nestables = array(); protected $valid_attributes = array('voice', 'language', 'loop'); function __construct($body, $attributes = array()) { if (!$body) { throw new PlivoError("No text set for ".$this->getName()); } else { $body = mb_encode_numericentity($body, array(0x80, 0xffff, 0, 0xffff)); } parent::__construct($body, $attributes); }}class Play extends Element { protected $nestables = array(); protected $valid_attributes = array('loop'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No url set for ".$this->getName()); } }}class Wait extends Element { protected $nestables = array(); protected $valid_attributes = array('length', 'silence', 'min_silence', 'minSilence', 'beep'); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class Redirect extends Element { protected $nestables = array(); protected $valid_attributes = array('method'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No url set for ".$this->getName()); } }}class Hangup extends Element { protected $nestables = array(); protected $valid_attributes = array('schedule', 'reason'); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class GetDigits extends Element { protected $nestables = array('Speak', 'Play', 'Wait'); protected $valid_attributes = array( 'action', 'method', 'timeout', 'digitTimeout', 'numDigits', 'retries', 'invalidDigitsSound', 'validDigits', 'playBeep', 'redirect', "finishOnKey", 'digitTimeout', 'log' ); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class Number extends Element { protected $nestables = array(); protected $valid_attributes = array('sendDigits', 'sendOnPreanswer', 'sendDigitsMode'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No number set for ".$this->getName()); } }}class User extends Element { protected $nestables = array(); protected $valid_attributes = array('sendDigits', 'sendOnPreanswer', 'sipHeaders'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No user set for ".$this->getName()); } }}class Dial extends Element { protected $nestables = array('Number', 'User'); protected $valid_attributes = array( 'action', 'method', 'timeout', 'hangupOnStar', 'timeLimit', 'callerId', 'callerName', 'confirmSound', 'dialMusic', 'confirmKey', 'redirect', 'callbackUrl', 'callbackMethod', 'digitsMatch', 'digitsMatchBLeg', 'sipHeaders' ); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class Conference extends Element { protected $nestables = array(); protected $valid_attributes = array( 'muted', 'beep', 'startConferenceOnEnter', 'endConferenceOnExit', 'waitSound', 'enterSound', 'exitSound', 'timeLimit', 'hangupOnStar', 'maxMembers', 'record', 'recordFileFormat', 'recordWhenAlone', 'action', 'method', 'redirect', 'digitsMatch', 'callbackUrl', 'callbackMethod', 'stayAlone', 'floorEvent', 'transcriptionType', 'transcriptionUrl', 'transcriptionMethod', 'relayDTMF' ); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No conference name set for ".$this->getName()); } }}class Record extends Element { protected $nestables = array(); protected $valid_attributes = array( 'action', 'method', 'timeout', 'finishOnKey', 'maxLength', 'playBeep', 'recordSession', 'startOnDialAnswer', 'redirect', 'fileFormat', 'callbackUrl', 'callbackMethod', 'transcriptionType', 'transcriptionUrl', 'transcriptionMethod' ); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class PreAnswer extends Element { protected $nestables = array('Play', 'Speak', 'GetDigits', 'Wait', 'Redirect', 'Message', 'DTMF'); protected $valid_attributes = array(); function __construct($attributes = array()) { parent::__construct(null, $attributes); }}class Message extends Element { protected $nestables = array(); protected $valid_attributes = array('src', 'dst', 'type', 'callbackMethod', 'callbackUrl'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No text set for ".$this->getName()); } }}class DTMF extends Element { protected $nestables = array(); protected $valid_attributes = array('async'); function __construct($body, $attributes = array()) { parent::__construct($body, $attributes); if (!$body) { throw new PlivoError("No digits set for ".$this->getName()); } }}