Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:02 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 16119)
+++ branches/5.2.x/core/units/helpers/curl_helper.php (revision 16120)
@@ -1,578 +1,580 @@
<?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;
/**
* 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 ();
}
/**
* Resets information in last* properties.
*
* @return void
*/
protected function resetLastInfo()
{
$this->lastErrorCode = 0;
$this->lastErrorMsg = '';
$this->lastHTTPCode = 0;
$this->lastRedirectCount = 0;
}
/**
* 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,
CURLOPT_MAXREDIRS => 5,
// don't verify SSL certificates
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
+
+ // Prevents CURL from adding "Expect: 100-continue" header for POST requests.
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 . '=' . kUtil::escape($value, kUtil::ESCAPE_URL) . '&';
}
$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
*
* @throws InvalidArgumentException When invalid request method given.
*/
public function SetRequestMethod($request_method)
{
if ($request_method != self::REQUEST_METHOD_GET && $request_method != self::REQUEST_METHOD_POST) {
throw new InvalidArgumentException('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 = '')
{
if ( isset($log_status) ) {
// override debug mode setting
$this->debugMode = $log_status;
}
$request_url = $url;
if ( $this->requestMethod == self::REQUEST_METHOD_GET && $this->requestData ) {
$request_url .= (strpos($request_url, '?') !== false ? '&' : '?') . $this->requestData;
}
$this->connectionID = curl_init($request_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->_sendRequest();
$this->Finalize($close_connection);
return $this->lastResponse;
}
/**
* Reads data from remote url
*
* @return string
* @access protected
*/
protected function _sendRequest()
{
$this->resetLastInfo();
curl_setopt($this->connectionID, CURLOPT_RETURNTRANSFER, 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 ( $this->lastRedirectCount == $this->options[CURLOPT_MAXREDIRS] ) {
return $this->setError(CURLE_TOO_MANY_REDIRECTS, 'Maximum (' . $this->options[CURLOPT_MAXREDIRS] . ') redirects followed');
}
if ( isset($url_parsed) ) {
curl_setopt($this->connectionID, CURLOPT_URL, $url);
$this->lastRedirectCount++;
return $this->_followLocationManually();
}
}
list(, $body) = explode("\r\n\r\n", $data, 2);
return $body;
}
/**
* Sets error manually.
*
* @param integer $code Code.
* @param string $message Message.
*
* @return boolean
*/
protected function setError($code, $message)
{
$this->lastErrorCode = $code;
$this->lastErrorMsg = $message;
return false;
}
/**
* 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)
{
if ( $this->lastErrorCode == 0 ) {
// error not set manually -> get it from curl
$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);
}
}

Event Timeline