00001 <?php
00007 namespace Habari;
00015 abstract class RequestProcessor
00016 {
00017
00018 protected $response_body = '';
00019 protected $response_headers = array();
00020
00021 protected $executed = false;
00022
00023 protected $can_followlocation = true;
00024
00025 abstract public function execute( $method, $url, $headers, $body, $config );
00026
00027 public function get_response_body ( ) {
00028
00029 if ( !$this->executed ) {
00030 throw new \Exception( _t( 'Unable to get response body. Request did not yet execute.' ) );
00031 }
00032
00033 return $this->response_body;
00034
00035 }
00036 public function get_response_headers ( ) {
00037
00038 if ( !$this->executed ) {
00039 throw new \Exception( _t( 'Unable to get response headers. Request did not yet execute.' ) );
00040 }
00041
00042 return $this->response_headers;
00043
00044 }
00045 }
00046
00051 class RemoteRequest
00052 {
00053 private $method = 'GET';
00054 private $url;
00055 private $params = array();
00056 private $headers = array(
00057 'Accept' => '*/*',
00058 );
00059 private $postdata = array();
00060 private $files = array();
00061 private $body = '';
00062 private $processor = null;
00063 private $executed = false;
00064
00065 private $response_body = '';
00066 private $response_headers = '';
00067
00068 private $user_agent = 'Habari';
00069
00070
00071 private $config = array(
00072 'connect_timeout' => 30,
00073 'timeout' => 180,
00074 'buffer_size' => 16384,
00075 'follow_redirects' => true,
00076 'max_redirects' => 5,
00077
00078
00079 'proxy' => array(
00080 'server' => null,
00081 'port' => null,
00082 'username' => null,
00083 'password' => null,
00084 'auth_type' => 'basic',
00085 'exceptions' => array(),
00086 'type' => 'http',
00087 ),
00088
00089
00090 'ssl' => array(
00091 'verify_peer' => true,
00092 'verify_host' => 2,
00093 'cafile' => null,
00094 'capath' => null,
00095 'local_cert' => null,
00096 'passphrase' => null,
00097
00098 ),
00099 );
00100
00107 public function __construct( $url, $method = 'GET', $timeout = 180 )
00108 {
00109 $this->method = strtoupper( $method );
00110 $this->url = $url;
00111 $this->set_timeout( $timeout );
00112
00113
00114 $default = new \stdClass();
00115 $proxy = Config::get( 'proxy', $default );
00116 if ( isset( $proxy->server ) ) {
00117 $this->set_config( array( 'proxy' => (array)$proxy ) );
00118 }
00119
00120
00121 $this->config['proxy']['exceptions'] = array_merge( $this->config['proxy']['exceptions'], array(
00122 'localhost',
00123 '127.0.0.1',
00124 '::1',
00125 ) );
00126
00127
00128 if ( isset( $_SERVER['SERVER_NAME'] ) ) {
00129 $this->config['proxy']['exceptions'][] = $_SERVER['SERVER_NAME'];
00130 }
00131
00132 if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
00133 $this->config['proxy']['exceptions'][] = $_SERVER['SERVER_ADDR'];
00134 }
00135
00136 $this->user_agent .= '/' . Version::get_habariversion();
00137 $this->add_header( array( 'User-Agent' => $this->user_agent ) );
00138
00139
00140 if ( Config::get( 'remote_request_processor' ) == 'socket' ) {
00141 $this->processor = new SocketRequestProcessor();
00142 }
00143 else {
00144
00145 if ( function_exists( 'curl_init' )
00146 && ! ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) ) {
00147 $this->processor = new CURLRequestProcessor;
00148 }
00149 else {
00150 $this->processor = new SocketRequestProcessor;
00151 }
00152
00153 }
00154 }
00155
00160 public function __set_processor( $processor )
00161 {
00162 $this->processor = $processor;
00163 }
00164
00171 public function set_config( $config, $value = null )
00172 {
00173 if ( is_array( $config ) ) {
00174 foreach ( $config as $name => $value ) {
00175 $this->set_config( $name, $value );
00176 }
00177 }
00178 else {
00179 if ( is_array( $value ) ) {
00180 $this->config[ $config ] = array_merge( $this->config[ $config ], $value );
00181 }
00182 else {
00183 $this->config[ $config ] = $value;
00184 }
00185 }
00186 }
00187
00192 public function add_header( $header )
00193 {
00194 if ( is_array( $header ) ) {
00195 $this->headers = array_merge( $this->headers, $header );
00196 }
00197 else {
00198 list( $k, $v )= explode( ': ', $header );
00199 $this->headers[$k] = $v;
00200 }
00201 }
00202
00207 public function add_headers( $headers )
00208 {
00209 foreach ( $headers as $header ) {
00210 $this->add_header( $header );
00211 }
00212 }
00213
00220 public function set_body( $body )
00221 {
00222 if ( $this->method == 'GET' ) {
00223 throw new \Exception( _t( 'Trying to add a request body to a GET request.' ) );
00224 }
00225
00226 $this->body = $body;
00227 }
00228
00234 public function set_params( $params )
00235 {
00236 if ( ! is_array( $params ) ) {
00237 parse_str( $params, $params );
00238 }
00239
00240 $this->params = $params;
00241 }
00242
00247 public function set_timeout( $timeout )
00248 {
00249 $this->config['timeout'] = $timeout;
00250 return $this->config['timeout'];
00251 }
00252
00260 public function set_postdata( $name, $value = null )
00261 {
00262 if ( is_array( $name ) ) {
00263 $this->postdata = array_merge( $this->postdata, $name );
00264 }
00265 else {
00266 $this->postdata[$name] = $value;
00267 }
00268 }
00269
00280 public function set_file( $name, $filename, $content_type = null, $override_filename = null )
00281 {
00282 if ( !file_exists( $filename ) ) {
00283 throw new \Exception( _t( 'File %s not found.', array( $filename ) ) );
00284 }
00285 if ( empty( $content_type ) ) $content_type = 'application/octet-stream';
00286 $this->files[$name] = array( 'filename' => $filename, 'content_type' => $content_type, 'override_filename' => $override_filename );
00287 $this->headers['Content-Type'] = 'multipart/form-data';
00288 }
00289
00293 private function prepare()
00294 {
00295
00296 $this->url = $this->strip_anchors( $this->url );
00297
00298 $this->url = $this->merge_query_params( $this->url, $this->params );
00299
00300 if ( $this->method === 'POST' ) {
00301 if ( !isset( $this->headers['Content-Type'] ) || ( $this->headers['Content-Type'] == 'application/x-www-form-urlencoded' ) ) {
00302
00303 $this->add_header( array( 'Content-Type' => 'application/x-www-form-urlencoded' ) );
00304
00305 if ( $this->body != '' && count( $this->postdata ) > 0 ) {
00306 $this->body .= '&';
00307 }
00308 $this->body .= http_build_query( $this->postdata, '', '&' );
00309 }
00310 elseif ( $this->headers['Content-Type'] == 'multipart/form-data' ) {
00311 $boundary = md5( Utils::nonce() );
00312 $this->headers['Content-Type'] .= '; boundary=' . $boundary;
00313
00314 $parts = array();
00315 if ( $this->postdata && is_array( $this->postdata ) ) {
00316 reset( $this->postdata );
00317 while ( list( $name, $value ) = each( $this->postdata ) ) {
00318 $parts[] = "Content-Disposition: form-data; name=\"{$name}\"\r\n\r\n{$value}\r\n";
00319 }
00320 }
00321
00322 if ( $this->files && is_array( $this->files ) ) {
00323 reset( $this->files );
00324 while ( list( $name, $fileinfo ) = each( $this->files ) ) {
00325 $filename = basename( $fileinfo['filename'] );
00326 if ( !empty( $fileinfo['override_filename'] ) ) {
00327 $filename = $fileinfo['override_filename'];
00328 }
00329 $part = "Content-Disposition: form-data; name=\"{$name}\"; filename=\"{$filename}\"\r\n";
00330 $part .= "Content-Type: {$fileinfo['content_type']}\r\n\r\n";
00331 $part .= file_get_contents( $fileinfo['filename'] ) . "\r\n";
00332 $parts[] = $part;
00333 }
00334 }
00335
00336 if ( !empty( $parts ) ) {
00337 $this->body = "--{$boundary}\r\n" . join( "--{$boundary}\r\n", $parts ) . "--{$boundary}--\r\n";
00338 }
00339 }
00340 $this->add_header( array( 'Content-Length' => strlen( $this->body ) ) );
00341 }
00342 }
00343
00351 public function execute()
00352 {
00353 $this->prepare();
00354 $result = $this->processor->execute( $this->method, $this->url, $this->headers, $this->body, $this->config );
00355
00356 if ( $result ) {
00357 $this->response_headers = $this->processor->get_response_headers();
00358 $this->response_body = $this->processor->get_response_body();
00359 $this->executed = true;
00360
00361 return true;
00362 }
00363 else {
00364
00365 $this->executed = false;
00366
00367 return $result;
00368 }
00369 }
00370
00371 public function executed()
00372 {
00373 return $this->executed;
00374 }
00375
00380 public function get_response_headers()
00381 {
00382 if ( !$this->executed ) {
00383 throw new \Exception( _t( 'Unable to fetch response headers for a pending request.' ) );
00384 }
00385
00386 return $this->response_headers;
00387 }
00388
00393 public function get_response_body()
00394 {
00395 if ( !$this->executed ) {
00396 throw new \Exception( _t( 'Unable to fetch response body for a pending request.' ) );
00397 }
00398
00399 return $this->response_body;
00400 }
00401
00405 private function strip_anchors( $url )
00406 {
00407 return preg_replace( '/(#.*?)?$/', '', $url );
00408 }
00409
00413 private function __filter( $data, $url )
00414 {
00415 return Plugins::filter( 'remoterequest', $data, $url );
00416 }
00417
00423 private function merge_query_params( $url, $params )
00424 {
00425 $urlparts = InputFilter::parse_url( $url );
00426
00427 if ( ! isset( $urlparts['query'] ) ) {
00428 $urlparts['query'] = '';
00429 }
00430
00431 if ( ! is_array( $params ) ) {
00432 parse_str( $params, $params );
00433 }
00434
00435 $urlparts['query'] = http_build_query( array_merge( Utils::get_params( $urlparts['query'] ), $params ), '', '&' );
00436
00437 return InputFilter::glue_url( $urlparts );
00438 }
00439
00453 public static function get_contents( $url, $use_include_path = false, $context = null, $offset = 0, $maxlen = -1 )
00454 {
00455 try {
00456 $rr = new RemoteRequest( $url );
00457 if ( $rr->execute() === true ) {
00458 return ( $maxlen != -1
00459 ? MultiByte::substr( $rr->get_response_body(), $offset, $maxlen )
00460 : MultiByte::substr( $rr->get_response_body(), $offset ) );
00461 }
00462 else {
00463 return false;
00464 }
00465 }
00466 catch ( \Exception $e ) {
00467
00468
00469 return false;
00470 }
00471 }
00472
00473 }
00474
00475 class RemoteRequest_Timeout extends \Exception { }
00476
00477 ?>