Merge pull request #15934 from eileenmcnaughton/part_sane
[civicrm-core.git] / CRM / Utils / HttpClient.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
e647e388
TO
13 * This class handles HTTP downloads
14 *
15 * FIXME: fetch() and get() report errors differently -- e.g.
16 * fetch() returns fatal and get() returns an error code. Should
17 * refactor both (or get a third-party HTTP library) but don't
18 * want to deal with that so late in the 4.3 dev cycle.
6a488035
TO
19 *
20 * @package CRM
ca5cec67 21 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
22 */
23class CRM_Utils_HttpClient {
24
25 const STATUS_OK = 'ok';
26 const STATUS_WRITE_ERROR = 'write-error';
27 const STATUS_DL_ERROR = 'dl-error';
28
3b6f287b
TO
29 /**
30 * @var CRM_Utils_HttpClient
31 */
32 protected static $singleton;
33
afcc9be0 34 /**
e97c66ff 35 * @var int|null
50bfb460 36 * seconds; or NULL to use system default
afcc9be0 37 */
021cfda1 38 protected $connectionTimeout;
afcc9be0 39
5bc392e6
EM
40 /**
41 * @return CRM_Utils_HttpClient
42 */
3b6f287b
TO
43 public static function singleton() {
44 if (!self::$singleton) {
45 self::$singleton = new CRM_Utils_HttpClient();
46 }
47 return self::$singleton;
48 }
49
5bc392e6
EM
50 /**
51 * @param null $connectionTimeout
52 */
021cfda1
TO
53 public function __construct($connectionTimeout = NULL) {
54 $this->connectionTimeout = $connectionTimeout;
afcc9be0
TO
55 }
56
6a488035
TO
57 /**
58 * Download the remote zipfile.
59 *
77855840
TO
60 * @param string $remoteFile
61 * URL of a .zip file.
62 * @param string $localFile
63 * Path at which to store the .zip file.
6a488035
TO
64 * @return STATUS_OK|STATUS_WRITE_ERROR|STATUS_DL_ERROR
65 */
3b6f287b 66 public function fetch($remoteFile, $localFile) {
6a488035
TO
67 // Download extension zip file ...
68 if (!function_exists('curl_init')) {
69 CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
70 }
e647e388
TO
71
72 list($ch, $caConfig) = $this->createCurl($remoteFile);
6a488035
TO
73 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
74 CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
75 }
76
6a488035
TO
77 $fp = @fopen($localFile, "w");
78 if (!$fp) {
6a488035
TO
79 return self::STATUS_WRITE_ERROR;
80 }
81 curl_setopt($ch, CURLOPT_FILE, $fp);
82
83 curl_exec($ch);
84 if (curl_errno($ch)) {
6a488035
TO
85 return self::STATUS_DL_ERROR;
86 }
87 else {
88 curl_close($ch);
89 }
90
91 fclose($fp);
92
93 return self::STATUS_OK;
94 }
afcc9be0
TO
95
96 /**
fe482240 97 * Send an HTTP GET for a remote resource.
afcc9be0 98 *
77855840
TO
99 * @param string $remoteFile
100 * URL of remote file.
a6c01b45
CW
101 * @return array
102 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
afcc9be0
TO
103 */
104 public function get($remoteFile) {
afcc9be0
TO
105 // Download extension zip file ...
106 if (!function_exists('curl_init')) {
dadcd1ac
FG
107 // CRM-13805
108 CRM_Core_Session::setStatus(
109 ts('As a result, actions like retrieving the CiviCRM news feed will fail. Talk to your server administrator or hosting company to rectify this.'),
110 ts('Curl is not installed')
111 );
be2fb01f 112 return [self::STATUS_DL_ERROR, NULL];
afcc9be0 113 }
e647e388
TO
114
115 list($ch, $caConfig) = $this->createCurl($remoteFile);
116
afcc9be0 117 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
50bfb460 118 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
be2fb01f 119 return [self::STATUS_DL_ERROR, NULL];
afcc9be0
TO
120 }
121
afcc9be0 122 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
afcc9be0
TO
123 $data = curl_exec($ch);
124 if (curl_errno($ch)) {
be2fb01f 125 return [self::STATUS_DL_ERROR, $data];
afcc9be0
TO
126 }
127 else {
128 curl_close($ch);
129 }
130
be2fb01f 131 return [self::STATUS_OK, $data];
e647e388
TO
132 }
133
49626e3d 134 /**
fe482240 135 * Send an HTTP POST for a remote resource.
49626e3d 136 *
77855840
TO
137 * @param string $remoteFile
138 * URL of a .zip file.
c490a46a 139 * @param array $params
f4aaa82a 140 *
a6c01b45
CW
141 * @return array
142 * array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
49626e3d
CW
143 */
144 public function post($remoteFile, $params) {
145 // Download extension zip file ...
146 if (!function_exists('curl_init')) {
147 //CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
be2fb01f 148 return [self::STATUS_DL_ERROR, NULL];
49626e3d
CW
149 }
150
151 list($ch, $caConfig) = $this->createCurl($remoteFile);
152
153 if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
50bfb460 154 // CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
be2fb01f 155 return [self::STATUS_DL_ERROR, NULL];
49626e3d
CW
156 }
157
158 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
e7292422
TO
159 curl_setopt($ch, CURLOPT_POST, TRUE);
160 curl_setopt($ch, CURLOPT_POST, count($params));
161 curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
49626e3d
CW
162 $data = curl_exec($ch);
163 if (curl_errno($ch)) {
be2fb01f 164 return [self::STATUS_DL_ERROR, $data];
49626e3d
CW
165 }
166 else {
167 curl_close($ch);
168 }
169
be2fb01f 170 return [self::STATUS_OK, $data];
49626e3d
CW
171 }
172
e647e388
TO
173 /**
174 * @param string $remoteFile
a6c01b45
CW
175 * @return array
176 * (0 => resource, 1 => CA_Config_Curl)
e647e388
TO
177 */
178 protected function createCurl($remoteFile) {
be2fb01f 179 $caConfig = CA_Config_Curl::probe([
aaffa79f 180 'verify_peer' => (bool) Civi::settings()->get('verifySSL'),
be2fb01f 181 ]);
e647e388
TO
182
183 $ch = curl_init();
184 curl_setopt($ch, CURLOPT_URL, $remoteFile);
185 curl_setopt($ch, CURLOPT_HEADER, FALSE);
186 curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
187 curl_setopt($ch, CURLOPT_VERBOSE, 0);
eb066397 188 if ($this->isRedirectSupported()) {
439b9688
LS
189 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
190 }
021cfda1
TO
191 if ($this->connectionTimeout !== NULL) {
192 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
193 }
e647e388
TO
194 if (preg_match('/^https:/', $remoteFile) && $caConfig->isEnableSSL()) {
195 curl_setopt_array($ch, $caConfig->toCurlOptions());
196 }
afcc9be0 197
be2fb01f 198 return [$ch, $caConfig];
afcc9be0
TO
199 }
200
5bc392e6
EM
201 /**
202 * @return bool
203 */
eb066397 204 public function isRedirectSupported() {
8c633404 205 return (ini_get('open_basedir') == '') && (ini_get('safe_mode') == 'Off' || ini_get('safe_mode') == '' || ini_get('safe_mode') === FALSE);
eb066397
TO
206 }
207
6a488035 208}