* sendRequest('...');
* }
* }
* ?>
*
*
* @copyright Copyright © 2012-2012 Kerio Technologies s.r.o.
* @license http://www.kerio.com/developers/license/sdk-agreement
* @version 1.4.0.234
*/
class KerioApi implements KerioApiInterface {
/**
* End-Line format
*/
const CRLF = "\r\n";
/**
* HTTP server status
*/
const HTTP_SERVER_OK = 200;
/**
* Library name
* @var string
*/
public $name = 'Kerio APIs Client Library for PHP';
/**
* Library version
* @var string
*/
public $version = '1.4.0.234';
/**
* Debug mode
* @var boolean
*/
private $debug = FALSE;
/**
* Unique id used in request
* @var integer
*/
private $requestId = 0;
/**
* Hostname
* @var string
*/
protected $hostname = '';
/**
* X-Token
* @var string
*/
protected $token = '';
/**
* Cookies
* @var string
*/
protected $cookies = '';
/**
* Application details
* @var array
*/
protected $application = array('name' => '', 'vendor' => '', 'version' => '');
/**
* JSON-RPC settings
* @var array
*/
protected $jsonRpc = array('version' => '', 'port' => '', 'api' => '');
/**
* HTTP headers
* @var array
*/
protected $headers = array();
/**
* Socket handler
* @var resource
*/
private $socketHandler = '';
/**
* Socket timeout
* @var integer
*/
private $timeout = '';
/**
* Class contructor.
*
* @param string Application name
* @param string Application vendor
* @param string Application version
* @return void
* @throws KerioApiException
*/
public function __construct($name, $vendor, $version) {
$this->checkPhpEnvironment();
$this->setApplication($name, $vendor, $version);
$this->setJsonRpc($this->jsonRpc['version'], $this->jsonRpc['port'], $this->jsonRpc['api']);
}
/**
* Check PHP environment.
*
* @param void
* @return void
*/
private function checkPhpEnvironment() {
if (version_compare(PHP_VERSION, '5.1.0', '<')) {
die(sprintf('
kerio-api-php error
Minimum PHP version required is 5.1.0. Your installation is %s.
Please, upgrade your PHP installation.', phpversion()));
}
if (FALSE === function_exists('openssl_open')) {
die('kerio-api-php error
Your PHP installation does not have OpenSSL enabled.
To configure OpenSSL support in PHP, please edit your php.ini config file and enable row with php_openssl module, e.g. extension=php_openssl.dll
For more information see http://www.php.net/manual/en/openssl.installation.php.');
}
if (FALSE === function_exists('json_decode')) {
die('kerio-api-php error
Your PHP installation does not have JSON enabled.
To configure JSON support in PHP, please edit your php.ini config file and enable row with php_json module, e.g. extension=php_json.dll
For more information see http://www.php.net/manual/en/json.installation.php.');
}
}
/**
* Set application to identify on server.
*
* @param string Application name
* @param string Vendor
* @param string Version
* @return void
* @throws KerioApiException
*/
private function setApplication($name, $vendor, $version) {
if (empty($name) && empty($vendor) && empty($version)) {
throw new KerioApiException('Application not defined.');
}
else {
$this->debug(sprintf("Registering application '%s' by '%s' version '%s'
", $name, $vendor, $version));
$this->application = array(
'name' => $name,
'vendor' => $vendor,
'version' => $version
);
}
}
/**
* Get application detail.
*
* @param void
* @return array Application details
*/
public final function getApplication() {
return $this->application;
}
/**
* Set JSON-RPC settings.
*
* @see class/KerioApiInterface::setJsonRpc()
* @param string JSON-RPC version
* @param integer JSON-RPC port
* @param string JSON-RPC URI
* @return void
* @throws KerioApiException
*/
public final function setJsonRpc($version, $port, $api) {
if (empty($version) && empty($port) && empty($api)) {
throw new KerioApiException('JSON-RPC not defined.');
}
else {
$this->debug(sprintf("Registering JSON-RPC %s on %s using port %d", $version, $api, $port));
$this->jsonRpc = array(
'version' => $version,
'port' => $port,
'api' => $api
);
}
}
/**
* Get JSON-RPC settings.
*
* @param void
* @return array JSON-RPC settings
*/
public final function getJsonRpc() {
return $this->jsonRpc;
}
/**
* Enable or disable of displaying debug messages.
*
* @param boolean
* @return void
*/
public final function setDebug($boolean) {
$this->debug = (bool) $boolean;
}
/**
* Get debug settings.
*
* @param void
* @return boolean
*/
public final function getDebug() {
return $this->debug;
}
/**
* Display a message if debug is TRUE.
*
* @param string Message
* @param string CSS class
* @return string Message in <div> tags
*/
public function debug($message, $css = 'debug') {
if ($this->debug) {
printf('%s
%s', $css, $message, "\n");
}
}
/**
* Get product API version.
*
* @param void
* @return integer API version
*/
public function getApiVersion() {
$method = 'Version.getApiVersion';
$response = $this->sendRequest($method);
return $response['apiVersion'];
}
/**
* Login method.
*
* @see class/KerioApiInterface::login()
* @param string Hostname
* @param string Username
* @param string Password
* @return array Result
* @throws KerioApiException
*/
public function login($hostname, $username, $password) {
$this->clean();
if (empty($hostname)) {
throw new KerioApiException('Cannot login. Hostname not set.');
}
elseif (empty($username)) {
throw new KerioApiException('Cannot login. Username not set.');
}
elseif (empty($this->application)) {
throw new KerioApiException('Cannot login. Application not defined.');
}
$this->setHostname($hostname);
$method = 'Session.login';
$params = array(
'userName' => $username,
'password' => $password,
'application' => $this->application
);
$response = $this->sendRequest($method, $params);
return $response;
}
/**
* Logout method.
*
* @see class/KerioApiInterface::logout()
* @param void
* @return array Result
*/
public function logout() {
$method = 'Session.logout';
$response = $this->sendRequest($method);
$this->clean();
return $response;
}
/**
* Clean data.
*
* @param void
* @return void
*/
public function clean() {
if ($this->token) {
$this->debug('Removing X-Token.');
$this->token = '';
}
if ($this->cookies) {
$this->debug('Removing Cookies.');
$this->cookies = '';
}
$this->hostname = '';
$this->socketHandler = '';
}
/**
* Get full HTTP request.
*
* @param string HTTP method [POST,GET,PUT]
* @param string HTTP body
* @return string HTTP request
* @throws KerioApiException
*/
protected function getHttpRequest($method, $body) {
/* Clean data */
$this->headers = array();
$bodyRequest = '';
$fullRequest = '';
/* Prepare headers and get request body*/
switch ($method) {
case 'POST': // common requests
$bodyRequest = $this->getHttpPostRequest($body);
break;
case 'GET': // download
$bodyRequest = $this->getHttpGetRequest($body);
break;
case 'PUT': // upload
$bodyRequest = $this->getHttpPutRequest($body);
break;
default:
throw new KerioApiException('Cannot send request, unknown method.');
}
/* Add port to headers if non-default is used */
$port = ($this->jsonRpc['port'] == 443)
? ''
: sprintf(':%d', $this->jsonRpc['port']);
/* Set common headers */
$this->headers['Host:'] = sprintf('%s%s', $this->hostname, $port);
$this->headers['Content-Length:'] = strlen($bodyRequest);
$this->headers['Connection:'] = 'close';
/* Set X-Token and Cookies */
if ($this->token) {
$this->headers['Cookie:'] = $this->cookies;
$this->headers['X-Token:'] = $this->token;
}
/* Build request */
foreach ($this->headers as $item => $value){
$fullRequest .= $item . ' ' . $value . self::CRLF;
}
$fullRequest .= self::CRLF;
$fullRequest .= $bodyRequest;
/* Return */
return $fullRequest;
}
/**
* Get headers for POST request.
*
* @param string Request body
* @return string Request body
*/
protected function getHttpPostRequest($data) {
$this->headers['POST'] = sprintf('%s HTTP/1.1', $this->jsonRpc['api']);
$this->headers['Accept:'] = 'application/json-rpc';
$this->headers['Content-Type:'] = 'application/json-rpc; charset=UTF-8';
$this->headers['User-Agent:'] = sprintf('%s/%s', $this->name, $this->version);
return str_replace(array("\r", "\r\n", "\n", "\t"), '', $data) . self::CRLF;
}
/**
* Get headers for GET request.
*
* @param string Request body
* @return string Request body
*/
protected function getHttpGetRequest($data) {
$this->headers['GET'] = sprintf('%s HTTP/1.1', $data);
$this->headers['Accept:'] = '*/*';
return $data . self::CRLF;
}
/**
* Get headers for PUT request.
*
* @param string Request body
* @return string Request body
*/
protected function getHttpPutRequest($data) {
$boundary = sprintf('---------------------%s', substr(md5(rand(0,32000)), 0, 10));
$this->headers['POST'] = sprintf('%s%s HTTP/1.1', $this->jsonRpc['api'], 'upload/');
$this->headers['Accept:'] = '*/*';
$this->headers['Content-Type:'] = sprintf('multipart/form-data; boundary=%s', $boundary);
$body = '--' . $boundary . self::CRLF;
$body .= 'Content-Disposition: form-data; name="unknown"; filename="newFile.bin"' . self::CRLF;
$body .= self::CRLF;
$body .= $data . self::CRLF;
$body .= '--' . $boundary . '--' . self::CRLF;
return $body;
}
/**
* Send request using method and its params.
*
* @see class/KerioApiInterface::sendRequest()
* @param string Interface.method
* @param array Params of 'Interface.method'.
* @return array Returns same type as param is, e.g. JSON if method is also JSON
*/
public function sendRequest($method, $params = '') {
$request = array(
'jsonrpc' => $this->jsonRpc['version'],
'id' => $this->getRequestId(),
'token' => $this->token,
'method' => $method,
'params' => $params
);
if (empty($this->token)) {
unset($request['token']);
}
if (empty($params)) {
unset($request['params']);
}
$json_request = json_encode($request);
/* Send data to server */
$json_response = $this->send('POST', $json_request);
/* Return */
$response = json_decode($json_response, TRUE);
return $response['result'];
}
/**
* Send JSON request.
*
* @param string JSON request
* @return string JSON response
*/
public function sendRequestJson($json) {
return $this->send('POST', $json);
}
/**
* Send data to server.
*
* @param string Request method [POST,GET,PUT]
* @param string Request body
* @return string Server response
* @throws KerioApiException
*/
protected function send($method, $data) {
if (empty($this->hostname)) {
throw new KerioApiException('Cannot send data before login.');
}
/* Get full HTTP request */
$request = $this->getHttpRequest($method, $data);
$this->debug(sprintf("→ Raw request:\n%s
", $request));
/* Open socket */
$this->socketHandler = new KerioApiSocket($this->hostname, $this->jsonRpc['port'], $this->timeout);
/* Send data */
$rawResponse = $this->socketHandler->send($request);
$this->debug(sprintf("← Raw response:\n%s
", $rawResponse));
/* Parse response */
$headers = $this->socketHandler->getHeaders();
$body = $this->socketHandler->getBody();
$this->checkHttpResponse(self::HTTP_SERVER_OK, $headers);
/* Decode JSON response */
$response = stripslashes($body);
$response = json_decode($body, TRUE);
if (($method == 'POST') && empty($response)) {
throw new KerioApiException('Invalid JSON data, cannot parse response.');
}
/* Set CSRF token */
if (empty($this->token)) {
if (isset($response['result']['token'])) {
$this->setToken($response['result']['token']);
}
}
/* Handle errors */
if (isset($response['error'])) {
if (FALSE === empty($response['error'])) {
$message = $response['error']['message'];
$code = $response['error']['code'];
$params = (isset($response['error']['data']))
? $response['error']['data']['messageParameters']['positionalParameters']
: '';
throw new KerioApiException($message, $code, $params, $data, $body);
}
}
elseif (isset($response['result']['errors'])) {
if (FALSE === empty($response['result']['errors'])) {
$message = $response['result']['errors'][0]['message'];
$code = $response['result']['errors'][0]['code'];
$params = $response['result']['errors'][0]['messageParameters']['positionalParameters'];
throw new KerioApiException($message, $code, $params, $data, $body);
}
}
/* Handle Cookies */
if (empty($this->cookies)) {
$this->setCookieFromHeaders($headers);
}
/* Return */
return $body;
}
/**
* Get a file from server.
*
* @param string File url
* @param string Save directory
* @param string Save as, optional. Default is file.bin
* @return boolean True on success
* @throws KerioApiException
*/
public function downloadFile($url, $directory, $filename = '') {
$saveAs = (empty($filename)) ? 'file.bin' : $filename;
$saveAs = sprintf('%s/%s', $directory, $filename);
$data = $this->send('GET', $url);
$this->debug(sprintf('Saving file %s', $saveAs));
if (FALSE === @file_put_contents($saveAs, $data)) {
throw new KerioApiException(sprintf('Unable to save file %s', $saveAs));
}
return TRUE;
}
/**
* Get a file from server.
*
* @param string File url
* @return string File content
*/
public function getFile($url) {
return $this->send('GET', $url);
}
/**
* Put a file to server.
*
* @param string Absolute path to file
* @param integer Reference ID where uploaded file belongs to, optional
* @return array Result
* @throws KerioApiException
*/
public function uploadFile($filename, $id = null) {
$data = @file_get_contents($filename);
if ($data) {
$this->debug(sprintf('Uploading file %s', $filename));
$json_response = $this->send('PUT', $data);
}
else {
throw new KerioApiException(sprintf('Unable to open file %s', $filename));
}
$response = json_decode($json_response, TRUE);
return $response['result'];
}
/**
* Check HTTP/1.1 reponse header.
*
* @param integer Requested HTTP code
* @param string HTTP headers
* @return boolean True if match
* @throws KerioApiException
*/
protected function checkHttpResponse($code, $headers) {
preg_match('#HTTP/\d+\.\d+ (\d+) (.+)#', $headers, $result);
switch ($result[1]) {
case $code:
return TRUE;
default:
$remote = sprintf('https://%s:%d%s', $this->hostname, $this->jsonRpc['port'], $this->jsonRpc['api']);
throw new KerioApiException(sprintf('%d - %s on remote server %s', $result[1], $result[2], $remote));
}
}
/**
* Set hostname.
*
* @param string Hostname
* @return void
*/
public function setHostname($hostname) {
$hostname = preg_split('/:/', $hostname);
$this->hostname = $hostname[0];
if (isset($hostname[1])) {
$this->setJsonRpc($this->jsonRpc['version'], $hostname[1], $this->jsonRpc['api']);
}
}
/**
* Get request ID.
*
* @param void
* @return integer
*/
private function getRequestId() {
$this->requestId++;
return $this->requestId;
}
/**
* Set security Cross-Site Request Forgery X-Token.
*
* @param string X-Token value
* @return void
*/
protected function setToken($token) {
$this->debug(sprintf('Setting X-Token %s.', $token));
$this->token = $token;
}
/**
* Get security Cross-Site Request Forgery X-Token.
*
* @param void
* @return string X-Token value
*/
public function getToken() {
return $this->token;
}
/**
* Set Cookies.
*
* @param string Cookies
* @return void
*/
protected function setCookie($cookies) {
$this->cookies = $cookies;
}
/**
* Get Cookies.
*
* @param void
* @return string Cookies
*/
public function getCookie() {
return $this->cookies;
}
/**
* Set Cookie from response.
*
* @param string HTTP headers
* @return void
*/
private function setCookieFromHeaders($headers) {
foreach (explode("\n", $headers) as $line) {
if (preg_match_all('/Set-Cookie:\s(\w*)=(\w*)/', $line, $result)) {
foreach ($result[1] as $index => $cookie) {
$this->debug(sprintf('Setting %s=%s.', $cookie, $result[2][$index]));
$this->setCookie(sprintf('%s %s=%s;', $this->getCookie(), $cookie, $result[2][$index]));
}
}
}
}
/**
* Set connection timeout.
*
* @param integer Timeout in seconds
* @return void
*/
public function setTimeout($timeout) {
$this->timeout = (integer) $timeout;
}
}