Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:06 AM

in-portal

Index: branches/5.2.x/core/units/helpers/curl_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/curl_helper.php (revision 15425)
+++ branches/5.2.x/core/units/helpers/curl_helper.php (revision 15426)
@@ -1,519 +1,531 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kCurlHelper extends kHelper {
const REQUEST_METHOD_GET = 1;
const REQUEST_METHOD_POST = 2;
/**
* ID of database record of currently active curl request
*
* @var int
* @access protected
*/
protected $logId = 0;
/**
* Connection to host
*
* @var resource
* @access protected
*/
- protected $connectionID = null;
+ protected $connectionID = NULL;
/**
* Response waiting timeout in seconds
*
* @var int
* @access public
*/
public $timeout = 90;
/**
* Follow to url, if redirect received instead of document (only works when open_basedir and safe mode is off)
*
* @var bool
* @access public
*/
public $followLocation = false;
/**
* Last response received by Curl
*
* @var string
* @access public
*/
public $lastResponse = '';
/**
* Last error code
*
* @var int
* @access public
*/
public $lastErrorCode = 0;
/**
* Last error message
*
* @var string
* @access public
*/
public $lastErrorMsg = '';
/**
* Most recent HTTP response code received
*
* @var int
* @access public
*/
public $lastHTTPCode = 0;
/**
* Count of intermediate redirects performed to get actual content
*
* @var int
* @access protected
*/
protected $lastRedirectCount = 0;
/**
* Default request method
*
* @var int
* @access protected
*/
protected $requestMethod = self::REQUEST_METHOD_GET;
/**
* Data to be sent using curl
*
* @var string
* @access protected
*/
protected $requestData = '';
/**
* Request headers (associative array)
*
* @var Array
* @access protected
*/
protected $requestHeaders = Array ();
/**
* Response headers
*
* @var Array
* @access protected
*/
protected $responseHeaders = Array ();
/**
* CURL options
*
* @var Array
* @access protected
*/
protected $options = Array ();
/**
* Indicates debug mode status
*
* @var bool
* @access public
*/
public $debugMode = false;
/**
* Creates an instance of kCurlHelper class
*/
public function __construct()
{
parent::__construct();
$this->debugMode = kUtil::constOn('DBG_CURL');
}
/**
* Reset connection settings (not results) after connection was closed
*
* @access protected
*/
protected function _resetSettings()
{
$this->timeout = 90;
$this->followLocation = false;
$this->requestMethod = self::REQUEST_METHOD_GET;
$this->requestData = '';
$this->requestHeaders = Array ();
$this->options = Array ();
}
/**
* Sets CURL options (adds to options set before)
*
* @param Array $options_hash
* @access public
*/
public function setOptions($options_hash)
{
$this->options = kUtil::array_merge_recursive($this->options, $options_hash);
}
/**
* Combines user-defined and default options before setting them to CURL
*
* @access protected
*/
protected function prepareOptions()
{
$default_options = Array (
// customizable options
CURLOPT_TIMEOUT => $this->timeout,
// hardcoded options
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_REFERER => PROTOCOL.SERVER_NAME,
// don't verify SSL certificates
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => Array ('Expect:'),
);
if ( isset($_SERVER['HTTP_USER_AGENT']) ) {
$default_options[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
}
if ($this->requestHeaders) {
$default_options[CURLOPT_HTTPHEADER] = $this->prepareHeaders();
}
// if we have post data, then POST else use GET method instead
if ($this->requestMethod == self::REQUEST_METHOD_POST) {
$default_options[CURLOPT_POST] = 1;
$default_options[CURLOPT_POSTFIELDS] = $this->requestData;
}
// $default_options[CURLOPT_HEADERFUNCTION] = Array(&$this, 'ParseHeader');
$user_options = $this->options; // backup options, that user set directly
$this->setOptions($default_options);
$this->setOptions($user_options);
$this->applyOptions();
}
/**
* Sets prepared options to CURL
*
* @access protected
*/
protected function applyOptions()
{
foreach ($this->options as $option_name => $option_value) {
curl_setopt($this->connectionID, $option_name, $option_value);
}
}
/**
* Parses headers from CURL request
*
* @param resource $ch
* @param string $header
* @return int
* @access protected
*/
protected function ParseHeader(&$ch, $header)
{
$this->responseHeaders[] = $header;
return strlen($header);
}
/**
* Sets request data for next query
*
* @param mixed $data Array or string
*/
public function SetRequestData($data)
{
if ( is_array($data) ) {
$params_str = '';
$data = $this->Application->HttpQuery->_transformArrays($data);
foreach ($data as $key => $value) {
$params_str .= $key . '=' . urlencode($value) . '&';
}
$data = $params_str;
}
$this->requestData = $data;
}
/**
* Sets request data for next query and switches request method to POST
*
* @param mixed $data Array or string
* @access public
*/
public function SetPostData($data)
{
$this->requestMethod = self::REQUEST_METHOD_POST;
$this->SetRequestData($data);
}
/**
* Sets request method to be used in next request
*
* @param int $request_method
*/
public function SetRequestMethod($request_method)
{
if ($request_method != self::REQUEST_METHOD_GET || $request_method != self::REQUEST_METHOD_POST) {
throw new Exception('Method "' . __METHOD__ . '": Invalid $request_method parameter value');
return ;
}
$this->requestMethod = $request_method;
}
/**
* Sets headers to be sent along with next query
*
* @param Array $headers
* @access public
*/
public function SetHeaders($headers)
{
$this->requestHeaders = array_merge($this->requestHeaders, $headers);
}
/**
* Returns compiled header to be used by curl
*
* @return Array
* @access protected
*/
protected function prepareHeaders()
{
$ret = Array ();
foreach ($this->requestHeaders as $header_name => $header_value) {
$ret[] = is_numeric($header_name) ? $header_value : $header_name . ': ' . $header_value;
}
return $ret;
}
/**
* Performs CURL request and returns it's result
*
* @param string $url
* @param bool $close_connection
* @param bool $log_status
* @param string $log_message
* @return string
* @access public
*/
- public function Send($url, $close_connection = true, $log_status = null, $log_message = '')
+ public function Send($url, $close_connection = true, $log_status = NULL, $log_message = '')
{
if ( isset($log_status) ) {
// override debug mode setting
$this->debugMode = $log_status;
}
$this->connectionID = curl_init($url);
if ( $this->debugMode ) {
// collect page data
$page_data = Array ();
if ( $_GET ) {
$page_data[] = '_GET:' . "\n" . print_r($_GET, true);
}
if ( $_POST ) {
$page_data[] = '_POST:' . "\n" . print_r($_POST, true);
}
if ( $_COOKIE ) {
$page_data[] = '_COOKIE:' . "\n" . print_r($_COOKIE, true);
}
// create log record
$fields_hash = Array (
'Message' => $log_message,
'PageUrl' => $_SERVER['REQUEST_URI'],
'RequestUrl' => $url,
'PortalUserId' => $this->Application->RecallVar('user_id'),
'SessionKey' => $this->Application->GetSID(),
'IsAdmin' => $this->Application->isAdminUser ? 1 : 0,
'PageData' => implode("\n", $page_data),
'RequestData' => $this->requestData,
'RequestDate' => adodb_mktime(),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CurlLog');
$this->logId = $this->Conn->getInsertID();
}
$this->responseHeaders = Array ();
$this->prepareOptions();
- $this->lastResponse = $this->execFollow();
+ $this->lastResponse = $this->_sendRequest();
$this->Finalize($close_connection);
return $this->lastResponse;
}
/**
- * Fixes curl inability to automatically follow location when safe_mode/open_basedir restriction in effect
+ * Reads data from remote url
*
- * @param bool $headers_only
* @return string
* @access protected
*/
- protected function execFollow($headers_only = false)
+ protected function _sendRequest()
{
- curl_setopt($this->connectionID, CURLOPT_HEADER, true);
curl_setopt($this->connectionID, CURLOPT_RETURNTRANSFER, true);
- if ( $this->followLocation && !$this->followLocationLimited() ) {
- // no restrictions - let curl do automatic redirects
- curl_setopt($this->connectionID, CURLOPT_FOLLOWLOCATION, true);
+ if ( $this->followLocation ) {
+ if ( $this->followLocationLimited() ) {
+ return $this->_followLocationManually();
+ }
+ else {
+ // no restrictions - let curl do automatic redirects
+ curl_setopt($this->connectionID, CURLOPT_FOLLOWLOCATION, true);
+ }
}
+ return curl_exec($this->connectionID);
+ }
+
+ /**
+ * Fixes curl inability to automatically follow location when safe_mode/open_basedir restriction in effect
+ *
+ * @return string
+ * @access protected
+ */
+ protected function _followLocationManually()
+ {
+ curl_setopt($this->connectionID, CURLOPT_HEADER, true);
$data = curl_exec($this->connectionID);
+
$http_code = $this->getInfo(CURLINFO_HTTP_CODE);
if ( $http_code == 301 || $http_code == 302 ) {
// safe more or open_basedir restriction - do redirects manually
list ($header) = explode("\r\n\r\n", $data, 2);
preg_match('/(Location:|URI:)(.*?)\n/', $header, $regs);
$url = trim(array_pop($regs));
$url_parsed = parse_url($url);
if ( isset($url_parsed) ) {
curl_setopt($this->connectionID, CURLOPT_URL, $url);
$this->lastRedirectCount++;
- return $this->execFollow($headers_only);
+ return $this->_followLocationManually();
}
}
- if ( $headers_only ) {
- return $data;
- }
-
list(, $body) = explode("\r\n\r\n", $data, 2);
return $body;
}
/**
* Returns various info about request made
*
* @param int $info_type
* @return mixed
*
* @see http://www.php.net/manual/ru/function.curl-getinfo.php
* @access public
*/
public function getInfo($info_type)
{
if ( $info_type == CURLINFO_REDIRECT_COUNT && $this->followLocationLimited() ) {
return $this->lastRedirectCount;
}
return curl_getinfo($this->connectionID, $info_type);
}
/**
* Detects, that follow location can't be done automatically by curl due safe_mode/open_basedir restrictions
*
* @return bool
* @access protected
*/
protected function followLocationLimited()
{
return (defined('SAFE_MODE') && SAFE_MODE) || ini_get('open_basedir');
}
/**
* Finalizes curl request and saves some data from curl before closing connection
*
* @param bool $close_connection
* @return void
* @access public
*/
public function Finalize($close_connection = true)
{
$this->lastErrorCode = curl_errno($this->connectionID);
$this->lastErrorMsg = curl_error($this->connectionID);
$this->lastHTTPCode = $this->getInfo(CURLINFO_HTTP_CODE);
if ( $close_connection ) {
$this->CloseConnection();
}
$this->_resetSettings();
}
/**
* Closes connection to server
*
* @access public
*/
public function CloseConnection()
{
curl_close($this->connectionID);
if ( $this->debugMode ) {
$fields_hash = Array (
'ResponseData' => $this->lastResponse,
'ResponseDate' => adodb_mktime(),
'ResponseHttpCode' => $this->lastHTTPCode,
'CurlError' => $this->lastErrorCode != 0 ? '#' . $this->lastErrorCode . ' (' . $this->lastErrorMsg . ')' : '',
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'CurlLog', 'LogId = ' . $this->logId);
}
// restore debug mode setting
$this->debugMode = kUtil::constOn('DBG_CURL');
}
/**
* Checks, that last curl request was successful
*
* @return bool
* @access public
*/
public function isGoodResponseCode()
{
if ( $this->lastErrorCode != 0 ) {
return false;
}
return ($this->lastHTTPCode == 200) || ($this->lastHTTPCode >= 300 && $this->lastHTTPCode < 310);
}
}
\ No newline at end of file

Event Timeline