PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/_assets/jk_campaignmonitor/class/transport.php

https://bitbucket.org/effectal/ondi
PHP | 282 lines | 214 code | 56 blank | 12 comment | 25 complexity | 09b2e5424177ddaf3badd9200d2cb4a6 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. define('CF7CM_CS_REST_GET', 'GET');
  3. define('CF7CM_CS_REST_POST', 'POST');
  4. define('CF7CM_CS_REST_PUT', 'PUT');
  5. define('CF7CM_CS_REST_DELETE', 'DELETE');
  6. define('CF7CM_CS_REST_SOCKET_TIMEOUT', 1);
  7. function CF7CM_CS_REST_TRANSPORT_get_available($requires_ssl, $log) {
  8. if(function_exists('curl_init') && function_exists('curl_exec')) {
  9. return new CF7CM_CS_REST_CurlTransport($log);
  10. } else if(CF7CM_CS_REST_TRANSPORT_can_use_raw_socket($requires_ssl)) {
  11. return new CF7CM_CS_REST_SocketTransport($log);
  12. } else {
  13. $log->log_message('No transport is available', __FUNCTION__, CF7CM_CS_REST_LOG_ERROR);
  14. trigger_error('No transport is available.'.
  15. ($requires_ssl ? ' Try using non-secure (http) mode or ' : ' Please ').
  16. 'ensure the cURL extension is loaded', E_USER_ERROR);
  17. }
  18. }
  19. function CF7CM_CS_REST_TRANSPORT_can_use_raw_socket($requires_ssl) {
  20. if(function_exists('fsockopen')) {
  21. if($requires_ssl) {
  22. return extension_loaded('openssl');
  23. }
  24. return true;
  25. }
  26. return false;
  27. }
  28. class CF7CM_CS_REST_BaseTransport {
  29. var $_log;
  30. function CF7CM_CS_REST_BaseTransport($log) {
  31. $this->_log = $log;
  32. }
  33. function split_and_inflate($response, $may_be_compressed) {
  34. list( $headers, $result ) = explode("\r\n\r\n", $response, 2);
  35. if($may_be_compressed && preg_match('/^Content-Encoding:\s+gzip\s+$/im', $headers)) {
  36. $original_length = strlen($response);
  37. $result = gzinflate(substr($result, 10, -8));
  38. $this->_log->log_message('Inflated gzipped response: '.$original_length.' bytes ->'.
  39. strlen($result).' bytes', get_class(), CF7CM_CS_REST_LOG_VERBOSE);
  40. }
  41. return array($headers, $result);
  42. }
  43. }
  44. /**
  45. * Provide HTTP request functionality via cURL extensions
  46. *
  47. * @author tobyb
  48. * @since 1.0
  49. */
  50. class CF7CM_CS_REST_CurlTransport extends CF7CM_CS_REST_BaseTransport {
  51. var $_curl_zlib;
  52. function CF7CM_CS_REST_CurlTransport($log) {
  53. $this->CF7CM_CS_REST_BaseTransport($log);
  54. $curl_version = curl_version();
  55. $this->_curl_zlib = isset($curl_version['libz_version']);
  56. }
  57. /**
  58. * @return string The type of transport used
  59. */
  60. function get_type() {
  61. return 'cURL';
  62. }
  63. function make_call($call_options) {
  64. $ch = curl_init();
  65. curl_setopt($ch, CURLOPT_URL, $call_options['route']);
  66. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  67. curl_setopt($ch, CURLOPT_HEADER, true);
  68. curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  69. curl_setopt($ch, CURLOPT_USERPWD, $call_options['credentials']);
  70. curl_setopt($ch, CURLOPT_USERAGENT, $call_options['userAgent']);
  71. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: '.$call_options['contentType']));
  72. $headers = array();
  73. $inflate_response = false;
  74. if($this->_curl_zlib) {
  75. $this->_log->log_message('curl+zlib support available. Requesting gzipped response.',
  76. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  77. curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
  78. } else if(function_exists('gzinflate')) {
  79. $headers[] = 'Accept-Encoding: gzip';
  80. $inflate_response = true;
  81. }
  82. if($call_options['protocol'] === 'https') {
  83. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
  84. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  85. curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__).'/GoDaddyClass2CA.crt');
  86. }
  87. switch($call_options['method']) {
  88. case CF7CM_CS_REST_PUT:
  89. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CF7CM_CS_REST_PUT);
  90. $headers[] = 'Content-Length: '.strlen($call_options['data']);
  91. curl_setopt($ch, CURLOPT_POSTFIELDS, $call_options['data']);
  92. break;
  93. case CF7CM_CS_REST_POST:
  94. curl_setopt($ch, CURLOPT_POST, true);
  95. curl_setopt($ch, CURLOPT_POSTFIELDS, $call_options['data']);
  96. break;
  97. case CF7CM_CS_REST_DELETE:
  98. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CF7CM_CS_REST_DELETE);
  99. break;
  100. }
  101. if(count($headers) > 0) {
  102. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  103. }
  104. $response = curl_exec($ch);
  105. if(!$response && $response !== '') {
  106. $this->_log->log_message('Error making request with curl_error: '.curl_errno($ch),
  107. get_class($this), CF7CM_CS_REST_LOG_ERROR);
  108. trigger_error('Error making request with curl_error: '.curl_error($ch), E_USER_ERROR);
  109. }
  110. list( $headers, $result ) = $this->split_and_inflate($response, $inflate_response);
  111. $this->_log->log_message('API Call Info for '.$call_options['method'].' '.
  112. curl_getinfo($ch, CURLINFO_EFFECTIVE_URL).': '.curl_getinfo($ch, CURLINFO_SIZE_UPLOAD).
  113. ' bytes uploaded. '.curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD).' bytes downloaded'.
  114. ' Total time (seconds): '.curl_getinfo($ch, CURLINFO_TOTAL_TIME),
  115. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  116. $result = array(
  117. 'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
  118. 'response' => $result
  119. );
  120. curl_close($ch);
  121. return $result;
  122. }
  123. }
  124. class CF7CM_CS_REST_SocketWrapper {
  125. var $socket;
  126. function open($domain, $port) {
  127. $this->socket = fsockopen($domain, $port, $errno, $errstr, CF7CM_CS_REST_SOCKET_TIMEOUT);
  128. if(!$this->socket) {
  129. die('Error making request with '.$errno.': '.$errstr);
  130. return false;
  131. } else if(function_exists('stream_set_timeout')) {
  132. stream_set_timeout($this->socket, CF7CM_CS_REST_SOCKET_TIMEOUT);
  133. }
  134. return true;
  135. }
  136. function write($data) {
  137. fwrite($this->socket, $data);
  138. }
  139. function read() {
  140. ob_start();
  141. fpassthru($this->socket);
  142. return ob_get_clean();
  143. }
  144. function close() {
  145. fclose($this->socket);
  146. }
  147. }
  148. class CF7CM_CS_REST_SocketTransport extends CF7CM_CS_REST_BaseTransport {
  149. var $_socket_wrapper;
  150. function CF7CM_CS_REST_SocketTransport($log, $socket_wrapper = NULL) {
  151. $this->CF7CM_CS_REST_BaseTransport($log);
  152. if(is_null($socket_wrapper)) {
  153. $socket_wrapper = new CF7CM_CS_REST_SocketWrapper();
  154. }
  155. $this->_socket_wrapper = $socket_wrapper;
  156. }
  157. /**
  158. * @return string The type of transport used
  159. */
  160. function get_type() {
  161. return 'Socket';
  162. }
  163. function make_call($call_options) {
  164. $start_host = strpos($call_options['route'], $call_options['host']);
  165. $host_len = strlen($call_options['host']);
  166. $domain = substr($call_options['route'], $start_host, $host_len);
  167. $host = $domain;
  168. $path = substr($call_options['route'], $start_host + $host_len);
  169. $protocol = substr($call_options['route'], 0, $start_host);
  170. $port = 80;
  171. $this->_log->log_message('Creating socket to '.$domain.' over '.$protocol.' for request to '.$path,
  172. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  173. if($protocol === 'https://') {
  174. $domain = 'ssl://'.$domain;
  175. $port = 443;
  176. }
  177. if($this->_socket_wrapper->open($domain, $port)) {
  178. $inflate_response = function_exists('gzinflate');
  179. $request = $this->_build_request($call_options, $host, $path, $inflate_response);
  180. $this->_log->log_message('Sending <pre>'.$request.'</pre> down the socket',
  181. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  182. $this->_socket_wrapper->write($request);
  183. $response = $this->_socket_wrapper->read();
  184. $this->_socket_wrapper->close();
  185. $this->_log->log_message('API Call Info for '.$call_options['method'].' '.
  186. $call_options['route'].': '.strlen($request).
  187. ' bytes uploaded. '.strlen($response).' bytes downloaded',
  188. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  189. list( $headers, $result ) = $this->split_and_inflate($response, $inflate_response);
  190. $this->_log->log_message('Received headers <pre>'.$headers.'</pre>',
  191. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  192. return array(
  193. 'code' => $this->_get_status_code($headers),
  194. 'response' => trim($result)
  195. );
  196. }
  197. }
  198. function _get_status_code($headers) {
  199. if (preg_match('%^\s*HTTP/1\.1 (?P<code>\d{3})%', $headers, $regs)) {
  200. $this->_log->log_message('Got HTTP Status Code: '.$regs['code'],
  201. get_class($this), CF7CM_CS_REST_LOG_VERBOSE);
  202. return $regs['code'];
  203. }
  204. $this->_log->log_message('Failed to get HTTP status code from request headers <pre>'.$headers.'</pre>',
  205. get_class($this), CF7CM_CS_REST_LOG_ERROR);
  206. trigger_error('Failed to get HTTP status code from request', E_USER_ERROR);
  207. }
  208. function _build_request($call_options, $host, $path, $accept_gzip) {
  209. $request =
  210. $call_options['method'].' '.$path." HTTP/1.1\n".
  211. 'Host: '.$host."\n".
  212. 'Authorization: Basic '.base64_encode($call_options['credentials'])."\n".
  213. 'User-Agent: '.$call_options['userAgent']."\n".
  214. 'Content-Type: '.$call_options['contentType']."\n";
  215. if($accept_gzip) {
  216. $request .=
  217. "Accept-Encoding: gzip\n";
  218. }
  219. if(isset($call_options['data'])) {
  220. $request .=
  221. 'Content-Length: '.strlen($call_options['data'])."\n\n".
  222. $call_options['data'];
  223. }
  224. return $request."\n\n";
  225. }
  226. }