3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
29 * This class handles HTTP downloads
31 * FIXME: fetch() and get() report errors differently -- e.g.
32 * fetch() returns fatal and get() returns an error code. Should
33 * refactor both (or get a third-party HTTP library) but don't
34 * want to deal with that so late in the 4.3 dev cycle.
37 * @copyright CiviCRM LLC (c) 2004-2018
39 class CRM_Utils_HttpClient
{
41 const STATUS_OK
= 'ok';
42 const STATUS_WRITE_ERROR
= 'write-error';
43 const STATUS_DL_ERROR
= 'dl-error';
46 * @var CRM_Utils_HttpClient
48 protected static $singleton;
52 * seconds; or NULL to use system default
54 protected $connectionTimeout;
57 * @return CRM_Utils_HttpClient
59 public static function singleton() {
60 if (!self
::$singleton) {
61 self
::$singleton = new CRM_Utils_HttpClient();
63 return self
::$singleton;
67 * @param null $connectionTimeout
69 public function __construct($connectionTimeout = NULL) {
70 $this->connectionTimeout
= $connectionTimeout;
74 * Download the remote zipfile.
76 * @param string $remoteFile
78 * @param string $localFile
79 * Path at which to store the .zip file.
80 * @return STATUS_OK|STATUS_WRITE_ERROR|STATUS_DL_ERROR
82 public function fetch($remoteFile, $localFile) {
83 // Download extension zip file ...
84 if (!function_exists('curl_init')) {
85 CRM_Core_Error
::fatal('Cannot install this extension - curl is not installed!');
88 list($ch, $caConfig) = $this->createCurl($remoteFile);
89 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
90 CRM_Core_Error
::fatal('Cannot install this extension - does not support SSL');
93 $fp = @fopen
($localFile, "w");
95 return self
::STATUS_WRITE_ERROR
;
97 curl_setopt($ch, CURLOPT_FILE
, $fp);
100 if (curl_errno($ch)) {
101 return self
::STATUS_DL_ERROR
;
109 return self
::STATUS_OK
;
113 * Send an HTTP GET for a remote resource.
115 * @param string $remoteFile
116 * URL of remote file.
118 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
120 public function get($remoteFile) {
121 // Download extension zip file ...
122 if (!function_exists('curl_init')) {
124 CRM_Core_Session
::setStatus(
125 ts('As a result, actions like retrieving the CiviCRM news feed will fail. Talk to your server administrator or hosting company to rectify this.'),
126 ts('Curl is not installed')
128 return array(self
::STATUS_DL_ERROR
, NULL);
131 list($ch, $caConfig) = $this->createCurl($remoteFile);
133 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
134 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
135 return array(self
::STATUS_DL_ERROR
, NULL);
138 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, 1);
139 $data = curl_exec($ch);
140 if (curl_errno($ch)) {
141 return array(self
::STATUS_DL_ERROR
, $data);
147 return array(self
::STATUS_OK
, $data);
151 * Send an HTTP POST for a remote resource.
153 * @param string $remoteFile
154 * URL of a .zip file.
155 * @param array $params
158 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
160 public function post($remoteFile, $params) {
161 // Download extension zip file ...
162 if (!function_exists('curl_init')) {
163 //CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
164 return array(self
::STATUS_DL_ERROR
, NULL);
167 list($ch, $caConfig) = $this->createCurl($remoteFile);
169 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
170 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
171 return array(self
::STATUS_DL_ERROR
, NULL);
174 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, 1);
175 curl_setopt($ch, CURLOPT_POST
, TRUE);
176 curl_setopt($ch, CURLOPT_POST
, count($params));
177 curl_setopt($ch, CURLOPT_POSTFIELDS
, $params);
178 $data = curl_exec($ch);
179 if (curl_errno($ch)) {
180 return array(self
::STATUS_DL_ERROR
, $data);
186 return array(self
::STATUS_OK
, $data);
190 * @param string $remoteFile
192 * (0 => resource, 1 => CA_Config_Curl)
194 protected function createCurl($remoteFile) {
195 $caConfig = CA_Config_Curl
::probe(array(
196 'verify_peer' => (bool) Civi
::settings()->get('verifySSL'),
200 curl_setopt($ch, CURLOPT_URL
, $remoteFile);
201 curl_setopt($ch, CURLOPT_HEADER
, FALSE);
202 curl_setopt($ch, CURLOPT_ENCODING
, 'gzip');
203 curl_setopt($ch, CURLOPT_VERBOSE
, 0);
204 if ($this->isRedirectSupported()) {
205 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, TRUE);
207 if ($this->connectionTimeout
!== NULL) {
208 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $this->connectionTimeout
);
210 if (preg_match('/^https:/', $remoteFile) && $caConfig->isEnableSSL()) {
211 curl_setopt_array($ch, $caConfig->toCurlOptions());
214 return array($ch, $caConfig);
220 public function isRedirectSupported() {
221 return (ini_get('open_basedir') == '') && (ini_get('safe_mode') == 'Off' ||
ini_get('safe_mode') == '' ||
ini_get('safe_mode') === FALSE);