KerioApi.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. <?php
  2. namespace ThurData\Servers\KerioEmail\Api;
  3. /**
  4. * This file is part of the kerio-api-php.
  5. *
  6. * Copyright (c) Kerio Technologies s.r.o.
  7. *
  8. * For the full copyright and license information, please view
  9. * the file license.txt that was distributed with this source code
  10. * or visit Developer Zone. (http://www.kerio.com/developers)
  11. *
  12. * Do not modify this source code.
  13. * Any changes may be overwritten by a new version.
  14. */
  15. require_once(dirname(__FILE__) . '/KerioApiInterface.php');
  16. require_once(dirname(__FILE__) . '/KerioApiSocket.php');
  17. require_once(dirname(__FILE__) . '/KerioApiException.php');
  18. /**
  19. * Kerio API Class.
  20. *
  21. * This is main class.
  22. *
  23. * Example:
  24. * <code>
  25. * <?php
  26. * require_once(dirname(__FILE__) . '/src/KerioApi.php');
  27. *
  28. * class MyApi extents KerioApi {
  29. *
  30. * public function __contruct($name, $vendor, $version) {
  31. * parent::__construct($name, $vendor, $version);
  32. * }
  33. *
  34. * public function getFoo() {
  35. * return $this->sendRequest('...');
  36. * }
  37. * }
  38. * ?>
  39. * </code>
  40. *
  41. * @copyright Copyright &copy; 2012-2012 Kerio Technologies s.r.o.
  42. * @license http://www.kerio.com/developers/license/sdk-agreement
  43. * @version 1.4.0.234
  44. */
  45. class KerioApi implements KerioApiInterface {
  46. /**
  47. * End-Line format
  48. */
  49. const CRLF = "\r\n";
  50. /**
  51. * HTTP server status
  52. */
  53. const HTTP_SERVER_OK = 200;
  54. /**
  55. * Library name
  56. * @var string
  57. */
  58. public $name = 'Kerio APIs Client Library for PHP';
  59. /**
  60. * Library version
  61. * @var string
  62. */
  63. public $version = '1.4.0.234';
  64. /**
  65. * Debug mode
  66. * @var boolean
  67. */
  68. private $debug = FALSE;
  69. /**
  70. * Unique id used in request
  71. * @var integer
  72. */
  73. private $requestId = 0;
  74. /**
  75. * Hostname
  76. * @var string
  77. */
  78. protected $hostname = '';
  79. /**
  80. * X-Token
  81. * @var string
  82. */
  83. protected $token = '';
  84. /**
  85. * Cookies
  86. * @var string
  87. */
  88. protected $cookies = '';
  89. /**
  90. * Application details
  91. * @var array
  92. */
  93. protected $application = array('name' => '', 'vendor' => '', 'version' => '');
  94. /**
  95. * JSON-RPC settings
  96. * @var array
  97. */
  98. protected $jsonRpc = array('version' => '', 'port' => '', 'api' => '');
  99. /**
  100. * HTTP headers
  101. * @var array
  102. */
  103. protected $headers = array();
  104. /**
  105. * Socket handler
  106. * @var resource
  107. */
  108. private $socketHandler = '';
  109. /**
  110. * Socket timeout
  111. * @var integer
  112. */
  113. private $timeout = '';
  114. /**
  115. * Class contructor.
  116. *
  117. * @param string Application name
  118. * @param string Application vendor
  119. * @param string Application version
  120. * @return void
  121. * @throws KerioApiException
  122. */
  123. public function __construct($name, $vendor, $version) {
  124. $this->checkPhpEnvironment();
  125. $this->setApplication($name, $vendor, $version);
  126. $this->setJsonRpc($this->jsonRpc['version'], $this->jsonRpc['port'], $this->jsonRpc['api']);
  127. }
  128. /**
  129. * Check PHP environment.
  130. *
  131. * @param void
  132. * @return void
  133. */
  134. private function checkPhpEnvironment() {
  135. if (version_compare(PHP_VERSION, '5.1.0', '<')) {
  136. die(sprintf('<h1>kerio-api-php error</h1>Minimum PHP version required is 5.1.0. Your installation is %s.<br>Please, upgrade your PHP installation.', phpversion()));
  137. }
  138. if (FALSE === function_exists('openssl_open')) {
  139. die('<h1>kerio-api-php error</h1>Your PHP installation does not have OpenSSL enabled.<br>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<br>For more information see <a href="http://www.php.net/manual/en/openssl.installation.php">http://www.php.net/manual/en/openssl.installation.php</a>.');
  140. }
  141. if (FALSE === function_exists('json_decode')) {
  142. die('<h1>kerio-api-php error</h1>Your PHP installation does not have JSON enabled.<br>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<br>For more information see <a href="http://www.php.net/manual/en/json.installation.php">http://www.php.net/manual/en/json.installation.php</a>.');
  143. }
  144. }
  145. /**
  146. * Set application to identify on server.
  147. *
  148. * @param string Application name
  149. * @param string Vendor
  150. * @param string Version
  151. * @return void
  152. * @throws KerioApiException
  153. */
  154. private function setApplication($name, $vendor, $version) {
  155. if (empty($name) && empty($vendor) && empty($version)) {
  156. throw new KerioApiException('Application not defined.');
  157. }
  158. else {
  159. $this->debug(sprintf("Registering application '%s' by '%s' version '%s'<br>", $name, $vendor, $version));
  160. $this->application = array(
  161. 'name' => $name,
  162. 'vendor' => $vendor,
  163. 'version' => $version
  164. );
  165. }
  166. }
  167. /**
  168. * Get application detail.
  169. *
  170. * @param void
  171. * @return array Application details
  172. */
  173. public final function getApplication() {
  174. return $this->application;
  175. }
  176. /**
  177. * Set JSON-RPC settings.
  178. *
  179. * @see class/KerioApiInterface::setJsonRpc()
  180. * @param string JSON-RPC version
  181. * @param integer JSON-RPC port
  182. * @param string JSON-RPC URI
  183. * @return void
  184. * @throws KerioApiException
  185. */
  186. public final function setJsonRpc($version, $port, $api) {
  187. if (empty($version) && empty($port) && empty($api)) {
  188. throw new KerioApiException('JSON-RPC not defined.');
  189. }
  190. else {
  191. $this->debug(sprintf("Registering JSON-RPC %s on %s using port %d", $version, $api, $port));
  192. $this->jsonRpc = array(
  193. 'version' => $version,
  194. 'port' => $port,
  195. 'api' => $api
  196. );
  197. }
  198. }
  199. /**
  200. * Get JSON-RPC settings.
  201. *
  202. * @param void
  203. * @return array JSON-RPC settings
  204. */
  205. public final function getJsonRpc() {
  206. return $this->jsonRpc;
  207. }
  208. /**
  209. * Enable or disable of displaying debug messages.
  210. *
  211. * @param boolean
  212. * @return void
  213. */
  214. public final function setDebug($boolean) {
  215. $this->debug = (bool) $boolean;
  216. }
  217. /**
  218. * Get debug settings.
  219. *
  220. * @param void
  221. * @return boolean
  222. */
  223. public final function getDebug() {
  224. return $this->debug;
  225. }
  226. /**
  227. * Display a message if debug is TRUE.
  228. *
  229. * @param string Message
  230. * @param string CSS class
  231. * @return string Message in &lt;div&gt; tags
  232. */
  233. public function debug($message, $css = 'debug') {
  234. if ($this->debug) {
  235. printf('<div class="%s">%s</div>%s', $css, $message, "\n");
  236. }
  237. }
  238. /**
  239. * Get product API version.
  240. *
  241. * @param void
  242. * @return integer API version
  243. */
  244. public function getApiVersion() {
  245. $method = 'Version.getApiVersion';
  246. $response = $this->sendRequest($method);
  247. return $response['apiVersion'];
  248. }
  249. /**
  250. * Login method.
  251. *
  252. * @see class/KerioApiInterface::login()
  253. * @param string Hostname
  254. * @param string Username
  255. * @param string Password
  256. * @return array Result
  257. * @throws KerioApiException
  258. */
  259. public function login($hostname, $username, $password) {
  260. $this->clean();
  261. if (empty($hostname)) {
  262. throw new KerioApiException('Cannot login. Hostname not set.');
  263. }
  264. elseif (empty($username)) {
  265. throw new KerioApiException('Cannot login. Username not set.');
  266. }
  267. elseif (empty($this->application)) {
  268. throw new KerioApiException('Cannot login. Application not defined.');
  269. }
  270. $this->setHostname($hostname);
  271. logModuleCall(
  272. 'kerioEmail',
  273. __FUNCTION__,
  274. $hostname,
  275. 'Debug Kerio Api 1',
  276. $this->application
  277. );
  278. $method = 'Session.login';
  279. $params = array(
  280. 'userName' => $username,
  281. 'password' => $password,
  282. 'application' => $this->application
  283. );
  284. $response = $this->sendRequest($method, $params);
  285. return $response;
  286. }
  287. /**
  288. * Logout method.
  289. *
  290. * @see class/KerioApiInterface::logout()
  291. * @param void
  292. * @return array Result
  293. */
  294. public function logout() {
  295. $method = 'Session.logout';
  296. $response = $this->sendRequest($method);
  297. $this->clean();
  298. return $response;
  299. }
  300. /**
  301. * Clean data.
  302. *
  303. * @param void
  304. * @return void
  305. */
  306. public function clean() {
  307. if ($this->token) {
  308. $this->debug('Removing X-Token.');
  309. $this->token = '';
  310. }
  311. if ($this->cookies) {
  312. $this->debug('Removing Cookies.');
  313. $this->cookies = '';
  314. }
  315. $this->hostname = '';
  316. $this->socketHandler = '';
  317. }
  318. /**
  319. * Get full HTTP request.
  320. *
  321. * @param string HTTP method [POST,GET,PUT]
  322. * @param string HTTP body
  323. * @return string HTTP request
  324. * @throws KerioApiException
  325. */
  326. protected function getHttpRequest($method, $body) {
  327. /* Clean data */
  328. $this->headers = array();
  329. $bodyRequest = '';
  330. $fullRequest = '';
  331. /* Prepare headers and get request body*/
  332. switch ($method) {
  333. case 'POST': // common requests
  334. $bodyRequest = $this->getHttpPostRequest($body);
  335. break;
  336. case 'GET': // download
  337. $bodyRequest = $this->getHttpGetRequest($body);
  338. break;
  339. case 'PUT': // upload
  340. $bodyRequest = $this->getHttpPutRequest($body);
  341. break;
  342. default:
  343. throw new KerioApiException('Cannot send request, unknown method.');
  344. }
  345. /* Add port to headers if non-default is used */
  346. $port = ($this->jsonRpc['port'] == 443)
  347. ? ''
  348. : sprintf(':%d', $this->jsonRpc['port']);
  349. /* Set common headers */
  350. $this->headers['Host:'] = sprintf('%s%s', $this->hostname, $port);
  351. $this->headers['Content-Length:'] = strlen($bodyRequest);
  352. $this->headers['Connection:'] = 'close';
  353. /* Set X-Token and Cookies */
  354. if ($this->token) {
  355. $this->headers['Cookie:'] = $this->cookies;
  356. $this->headers['X-Token:'] = $this->token;
  357. }
  358. /* Build request */
  359. foreach ($this->headers as $item => $value){
  360. $fullRequest .= $item . ' ' . $value . self::CRLF;
  361. }
  362. $fullRequest .= self::CRLF;
  363. $fullRequest .= $bodyRequest;
  364. /* Return */
  365. return $fullRequest;
  366. }
  367. /**
  368. * Get headers for POST request.
  369. *
  370. * @param string Request body
  371. * @return string Request body
  372. */
  373. protected function getHttpPostRequest($data) {
  374. $this->headers['POST'] = sprintf('%s HTTP/1.1', $this->jsonRpc['api']);
  375. $this->headers['Accept:'] = 'application/json-rpc';
  376. $this->headers['Content-Type:'] = 'application/json-rpc; charset=UTF-8';
  377. $this->headers['User-Agent:'] = sprintf('%s/%s', $this->name, $this->version);
  378. return str_replace(array("\r", "\r\n", "\n", "\t"), '', $data) . self::CRLF;
  379. }
  380. /**
  381. * Get headers for GET request.
  382. *
  383. * @param string Request body
  384. * @return string Request body
  385. */
  386. protected function getHttpGetRequest($data) {
  387. $this->headers['GET'] = sprintf('%s HTTP/1.1', $data);
  388. $this->headers['Accept:'] = '*/*';
  389. return $data . self::CRLF;
  390. }
  391. /**
  392. * Get headers for PUT request.
  393. *
  394. * @param string Request body
  395. * @return string Request body
  396. */
  397. protected function getHttpPutRequest($data) {
  398. $boundary = sprintf('---------------------%s', substr(md5(rand(0,32000)), 0, 10));
  399. $this->headers['POST'] = sprintf('%s%s HTTP/1.1', $this->jsonRpc['api'], 'upload/');
  400. $this->headers['Accept:'] = '*/*';
  401. $this->headers['Content-Type:'] = sprintf('multipart/form-data; boundary=%s', $boundary);
  402. $body = '--' . $boundary . self::CRLF;
  403. $body .= 'Content-Disposition: form-data; name="unknown"; filename="newFile.bin"' . self::CRLF;
  404. $body .= self::CRLF;
  405. $body .= $data . self::CRLF;
  406. $body .= '--' . $boundary . '--' . self::CRLF;
  407. return $body;
  408. }
  409. /**
  410. * Send request using method and its params.
  411. *
  412. * @see class/KerioApiInterface::sendRequest()
  413. * @param string Interface.method
  414. * @param array Params of 'Interface.method'.
  415. * @return array Returns same type as param is, e.g. JSON if method is also JSON
  416. */
  417. public function sendRequest($method, $params = '') {
  418. $request = array(
  419. 'jsonrpc' => $this->jsonRpc['version'],
  420. 'id' => $this->getRequestId(),
  421. 'token' => $this->token,
  422. 'method' => $method,
  423. 'params' => $params
  424. );
  425. if (empty($this->token)) {
  426. unset($request['token']);
  427. }
  428. if (empty($params)) {
  429. unset($request['params']);
  430. }
  431. $json_request = json_encode($request);
  432. /* Send data to server */
  433. $json_response = $this->send('POST', $json_request);
  434. /* Return */
  435. $response = json_decode($json_response, TRUE);
  436. return $response['result'];
  437. }
  438. /**
  439. * Send JSON request.
  440. *
  441. * @param string JSON request
  442. * @return string JSON response
  443. */
  444. public function sendRequestJson($json) {
  445. return $this->send('POST', $json);
  446. }
  447. /**
  448. * Send data to server.
  449. *
  450. * @param string Request method [POST,GET,PUT]
  451. * @param string Request body
  452. * @return string Server response
  453. * @throws KerioApiException
  454. */
  455. protected function send($method, $data) {
  456. if (empty($this->hostname)) {
  457. throw new KerioApiException('Cannot send data before login.');
  458. }
  459. /* Get full HTTP request */
  460. $request = $this->getHttpRequest($method, $data);
  461. $this->debug(sprintf("&rarr; Raw request:\n<pre>%s</pre>", $request));
  462. /* Open socket */
  463. $this->socketHandler = new KerioApiSocket($this->hostname, $this->jsonRpc['port'], $this->timeout);
  464. /* Send data */
  465. $rawResponse = $this->socketHandler->send($request);
  466. $this->debug(sprintf("&larr; Raw response:\n<pre>%s</pre>", $rawResponse));
  467. /* Parse response */
  468. $headers = $this->socketHandler->getHeaders();
  469. $body = $this->socketHandler->getBody();
  470. $this->checkHttpResponse(self::HTTP_SERVER_OK, $headers);
  471. /* Decode JSON response */
  472. $response = stripslashes($body);
  473. $response = json_decode($body, TRUE);
  474. if (($method == 'POST') && empty($response)) {
  475. throw new KerioApiException('Invalid JSON data, cannot parse response.');
  476. }
  477. /* Set CSRF token */
  478. if (empty($this->token)) {
  479. if (isset($response['result']['token'])) {
  480. $this->setToken($response['result']['token']);
  481. }
  482. }
  483. /* Handle errors */
  484. if (isset($response['error'])) {
  485. if (FALSE === empty($response['error'])) {
  486. $message = $response['error']['message'];
  487. $code = $response['error']['code'];
  488. $params = (isset($response['error']['data']))
  489. ? $response['error']['data']['messageParameters']['positionalParameters']
  490. : '';
  491. throw new KerioApiException($message, $code, $params, $data, $body);
  492. }
  493. }
  494. elseif (isset($response['result']['errors'])) {
  495. if (FALSE === empty($response['result']['errors'])) {
  496. $message = $response['result']['errors'][0]['message'];
  497. $code = $response['result']['errors'][0]['code'];
  498. $params = $response['result']['errors'][0]['messageParameters']['positionalParameters'];
  499. throw new KerioApiException($message, $code, $params, $data, $body);
  500. }
  501. }
  502. /* Handle Cookies */
  503. if (empty($this->cookies)) {
  504. $this->setCookieFromHeaders($headers);
  505. }
  506. /* Return */
  507. return $body;
  508. }
  509. /**
  510. * Get a file from server.
  511. *
  512. * @param string File url
  513. * @param string Save directory
  514. * @param string Save as, optional. Default is file.bin
  515. * @return boolean True on success
  516. * @throws KerioApiException
  517. */
  518. public function downloadFile($url, $directory, $filename = '') {
  519. $saveAs = (empty($filename)) ? 'file.bin' : $filename;
  520. $saveAs = sprintf('%s/%s', $directory, $filename);
  521. $data = $this->send('GET', $url);
  522. $this->debug(sprintf('Saving file %s', $saveAs));
  523. if (FALSE === @file_put_contents($saveAs, $data)) {
  524. throw new KerioApiException(sprintf('Unable to save file %s', $saveAs));
  525. }
  526. return TRUE;
  527. }
  528. /**
  529. * Get a file from server.
  530. *
  531. * @param string File url
  532. * @return string File content
  533. */
  534. public function getFile($url) {
  535. return $this->send('GET', $url);
  536. }
  537. /**
  538. * Put a file to server.
  539. *
  540. * @param string Absolute path to file
  541. * @param integer Reference ID where uploaded file belongs to, optional
  542. * @return array Result
  543. * @throws KerioApiException
  544. */
  545. public function uploadFile($filename, $id = null) {
  546. $data = @file_get_contents($filename);
  547. if ($data) {
  548. $this->debug(sprintf('Uploading file %s', $filename));
  549. $json_response = $this->send('PUT', $data);
  550. }
  551. else {
  552. throw new KerioApiException(sprintf('Unable to open file %s', $filename));
  553. }
  554. $response = json_decode($json_response, TRUE);
  555. return $response['result'];
  556. }
  557. /**
  558. * Check HTTP/1.1 reponse header.
  559. *
  560. * @param integer Requested HTTP code
  561. * @param string HTTP headers
  562. * @return boolean True if match
  563. * @throws KerioApiException
  564. */
  565. protected function checkHttpResponse($code, $headers) {
  566. preg_match('#HTTP/\d+\.\d+ (\d+) (.+)#', $headers, $result);
  567. switch ($result[1]) {
  568. case $code:
  569. return TRUE;
  570. default:
  571. $remote = sprintf('https://%s:%d%s', $this->hostname, $this->jsonRpc['port'], $this->jsonRpc['api']);
  572. throw new KerioApiException(sprintf('%d - %s on remote server %s', $result[1], $result[2], $remote));
  573. }
  574. }
  575. /**
  576. * Set hostname.
  577. *
  578. * @param string Hostname
  579. * @return void
  580. */
  581. public function setHostname($hostname) {
  582. $hostname = preg_split('/:/', $hostname);
  583. $this->hostname = $hostname[0];
  584. if (isset($hostname[1])) {
  585. $this->setJsonRpc($this->jsonRpc['version'], $hostname[1], $this->jsonRpc['api']);
  586. }
  587. }
  588. /**
  589. * Get request ID.
  590. *
  591. * @param void
  592. * @return integer
  593. */
  594. private function getRequestId() {
  595. $this->requestId++;
  596. return $this->requestId;
  597. }
  598. /**
  599. * Set security Cross-Site Request Forgery X-Token.
  600. *
  601. * @param string X-Token value
  602. * @return void
  603. */
  604. protected function setToken($token) {
  605. $this->debug(sprintf('Setting X-Token %s.', $token));
  606. $this->token = $token;
  607. }
  608. /**
  609. * Get security Cross-Site Request Forgery X-Token.
  610. *
  611. * @param void
  612. * @return string X-Token value
  613. */
  614. public function getToken() {
  615. return $this->token;
  616. }
  617. /**
  618. * Set Cookies.
  619. *
  620. * @param string Cookies
  621. * @return void
  622. */
  623. protected function setCookie($cookies) {
  624. $this->cookies = $cookies;
  625. }
  626. /**
  627. * Get Cookies.
  628. *
  629. * @param void
  630. * @return string Cookies
  631. */
  632. public function getCookie() {
  633. return $this->cookies;
  634. }
  635. /**
  636. * Set Cookie from response.
  637. *
  638. * @param string HTTP headers
  639. * @return void
  640. */
  641. private function setCookieFromHeaders($headers) {
  642. foreach (explode("\n", $headers) as $line) {
  643. if (preg_match_all('/Set-Cookie:\s(\w*)=(\w*)/', $line, $result)) {
  644. foreach ($result[1] as $index => $cookie) {
  645. $this->debug(sprintf('Setting %s=%s.', $cookie, $result[2][$index]));
  646. $this->setCookie(sprintf('%s %s=%s;', $this->getCookie(), $cookie, $result[2][$index]));
  647. }
  648. }
  649. }
  650. }
  651. /**
  652. * Set connection timeout.
  653. *
  654. * @param integer Timeout in seconds
  655. * @return void
  656. */
  657. public function setTimeout($timeout) {
  658. $this->timeout = (integer) $timeout;
  659. }
  660. }