Set version to 5.20.beta1
[civicrm-core.git] / CRM / Utils / HttpClient.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
e647e388
TO
29 * This class handles HTTP downloads
30 *
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.
6a488035
TO
35 *
36 * @package CRM
6b83d5bd 37 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
38 */
39class CRM_Utils_HttpClient {
40
41 const STATUS_OK = 'ok';
42 const STATUS_WRITE_ERROR = 'write-error';
43 const STATUS_DL_ERROR = 'dl-error';
44
3b6f287b
TO
45 /**
46 * @var CRM_Utils_HttpClient
47 */
48 protected static $singleton;
49
afcc9be0 50 /**
e97c66ff 51 * @var int|null
50bfb460 52 * seconds; or NULL to use system default
afcc9be0 53 */
021cfda1 54 protected $connectionTimeout;
afcc9be0 55
5bc392e6
EM
56 /**
57 * @return CRM_Utils_HttpClient
58 */
3b6f287b
TO
59 public static function singleton() {
60 if (!self::$singleton) {
61 self::$singleton = new CRM_Utils_HttpClient();
62 }
63 return self::$singleton;
64 }
65
5bc392e6
EM
66 /**
67 * @param null $connectionTimeout
68 */
021cfda1
TO
69 public function __construct($connectionTimeout = NULL) {
70 $this->connectionTimeout = $connectionTimeout;
afcc9be0
TO
71 }
72
6a488035
TO
73 /**
74 * Download the remote zipfile.
75 *
77855840
TO
76 * @param string $remoteFile
77 * URL of a .zip file.
78 * @param string $localFile
79 * Path at which to store the .zip file.
6a488035
TO
80 * @return STATUS_OK|STATUS_WRITE_ERROR|STATUS_DL_ERROR
81 */
3b6f287b 82 public function fetch($remoteFile, $localFile) {
6a488035
TO
83 // Download extension zip file ...
84 if (!function_exists('curl_init')) {
85 CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
86 }
e647e388
TO
87
88 list($ch, $caConfig) = $this->createCurl($remoteFile);
6a488035
TO
89 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
90 CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
91 }
92
6a488035
TO
93 $fp = @fopen($localFile, "w");
94 if (!$fp) {
6a488035
TO
95 return self::STATUS_WRITE_ERROR;
96 }
97 curl_setopt($ch, CURLOPT_FILE, $fp);
98
99 curl_exec($ch);
100 if (curl_errno($ch)) {
6a488035
TO
101 return self::STATUS_DL_ERROR;
102 }
103 else {
104 curl_close($ch);
105 }
106
107 fclose($fp);
108
109 return self::STATUS_OK;
110 }
afcc9be0
TO
111
112 /**
fe482240 113 * Send an HTTP GET for a remote resource.
afcc9be0 114 *
77855840
TO
115 * @param string $remoteFile
116 * URL of remote file.
a6c01b45
CW
117 * @return array
118 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
afcc9be0
TO
119 */
120 public function get($remoteFile) {
afcc9be0
TO
121 // Download extension zip file ...
122 if (!function_exists('curl_init')) {
dadcd1ac
FG
123 // CRM-13805
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')
127 );
be2fb01f 128 return [self::STATUS_DL_ERROR, NULL];
afcc9be0 129 }
e647e388
TO
130
131 list($ch, $caConfig) = $this->createCurl($remoteFile);
132
afcc9be0 133 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
50bfb460 134 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
be2fb01f 135 return [self::STATUS_DL_ERROR, NULL];
afcc9be0
TO
136 }
137
afcc9be0 138 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
afcc9be0
TO
139 $data = curl_exec($ch);
140 if (curl_errno($ch)) {
be2fb01f 141 return [self::STATUS_DL_ERROR, $data];
afcc9be0
TO
142 }
143 else {
144 curl_close($ch);
145 }
146
be2fb01f 147 return [self::STATUS_OK, $data];
e647e388
TO
148 }
149
49626e3d 150 /**
fe482240 151 * Send an HTTP POST for a remote resource.
49626e3d 152 *
77855840
TO
153 * @param string $remoteFile
154 * URL of a .zip file.
c490a46a 155 * @param array $params
f4aaa82a 156 *
a6c01b45
CW
157 * @return array
158 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
49626e3d
CW
159 */
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!');
be2fb01f 164 return [self::STATUS_DL_ERROR, NULL];
49626e3d
CW
165 }
166
167 list($ch, $caConfig) = $this->createCurl($remoteFile);
168
169 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
50bfb460 170 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
be2fb01f 171 return [self::STATUS_DL_ERROR, NULL];
49626e3d
CW
172 }
173
174 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
e7292422
TO
175 curl_setopt($ch, CURLOPT_POST, TRUE);
176 curl_setopt($ch, CURLOPT_POST, count($params));
177 curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
49626e3d
CW
178 $data = curl_exec($ch);
179 if (curl_errno($ch)) {
be2fb01f 180 return [self::STATUS_DL_ERROR, $data];
49626e3d
CW
181 }
182 else {
183 curl_close($ch);
184 }
185
be2fb01f 186 return [self::STATUS_OK, $data];
49626e3d
CW
187 }
188
e647e388
TO
189 /**
190 * @param string $remoteFile
a6c01b45
CW
191 * @return array
192 * (0 => resource, 1 => CA_Config_Curl)
e647e388
TO
193 */
194 protected function createCurl($remoteFile) {
be2fb01f 195 $caConfig = CA_Config_Curl::probe([
aaffa79f 196 'verify_peer' => (bool) Civi::settings()->get('verifySSL'),
be2fb01f 197 ]);
e647e388
TO
198
199 $ch = curl_init();
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);
eb066397 204 if ($this->isRedirectSupported()) {
439b9688
LS
205 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
206 }
021cfda1
TO
207 if ($this->connectionTimeout !== NULL) {
208 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
209 }
e647e388
TO
210 if (preg_match('/^https:/', $remoteFile) && $caConfig->isEnableSSL()) {
211 curl_setopt_array($ch, $caConfig->toCurlOptions());
212 }
afcc9be0 213
be2fb01f 214 return [$ch, $caConfig];
afcc9be0
TO
215 }
216
5bc392e6
EM
217 /**
218 * @return bool
219 */
eb066397 220 public function isRedirectSupported() {
8c633404 221 return (ini_get('open_basedir') == '') && (ini_get('safe_mode') == 'Off' || ini_get('safe_mode') == '' || ini_get('safe_mode') === FALSE);
eb066397
TO
222 }
223
6a488035 224}