1 | <?php |
||||
2 | |||||
3 | use Composer\CaBundle\CaBundle; |
||||
4 | |||||
5 | /** |
||||
6 | * This is a customised version of the mandrill api sdk available at |
||||
7 | * @https://bitbucket.org/mailchimp/mandrill-api-php/ |
||||
8 | * |
||||
9 | * - Fixes follow location (disable follow location in curl and replace curl_exec) |
||||
10 | * - Add IDE helpers |
||||
11 | * - Fix ca_cert.pem if not available |
||||
12 | */ |
||||
13 | require_once 'Mandrill/Templates.php'; |
||||
14 | require_once 'Mandrill/Exports.php'; |
||||
15 | require_once 'Mandrill/Users.php'; |
||||
16 | require_once 'Mandrill/Rejects.php'; |
||||
17 | require_once 'Mandrill/Inbound.php'; |
||||
18 | require_once 'Mandrill/Tags.php'; |
||||
19 | require_once 'Mandrill/Messages.php'; |
||||
20 | require_once 'Mandrill/Whitelists.php'; |
||||
21 | require_once 'Mandrill/Ips.php'; |
||||
22 | require_once 'Mandrill/Internal.php'; |
||||
23 | require_once 'Mandrill/Subaccounts.php'; |
||||
24 | require_once 'Mandrill/Urls.php'; |
||||
25 | require_once 'Mandrill/Webhooks.php'; |
||||
26 | require_once 'Mandrill/Senders.php'; |
||||
27 | require_once 'Mandrill/Metadata.php'; |
||||
28 | require_once 'Mandrill/Exceptions.php'; |
||||
29 | |||||
30 | /** |
||||
31 | * A mandrill api client |
||||
32 | */ |
||||
33 | class Mandrill |
||||
34 | { |
||||
35 | /** |
||||
36 | * @var string |
||||
37 | */ |
||||
38 | public $apikey; |
||||
39 | /** |
||||
40 | * @var resource|CurlHandle |
||||
41 | */ |
||||
42 | public $ch; |
||||
43 | /** |
||||
44 | * @var string |
||||
45 | */ |
||||
46 | public $root = 'https://mandrillapp.com/api/1.0'; |
||||
47 | /** |
||||
48 | * @var boolean |
||||
49 | */ |
||||
50 | public $debug = false; |
||||
51 | /** |
||||
52 | * @var Mandrill_Templates |
||||
53 | */ |
||||
54 | public $templates; |
||||
55 | /** |
||||
56 | * @var Mandrill_Exports |
||||
57 | */ |
||||
58 | public $exports; |
||||
59 | /** |
||||
60 | * @var Mandrill_Users |
||||
61 | */ |
||||
62 | public $users; |
||||
63 | /** |
||||
64 | * @var Mandrill_Rejects |
||||
65 | */ |
||||
66 | public $rejects; |
||||
67 | /** |
||||
68 | * @var Mandrill_Inbound |
||||
69 | */ |
||||
70 | public $inbound; |
||||
71 | /** |
||||
72 | * @var Mandrill_Tags |
||||
73 | */ |
||||
74 | public $tags; |
||||
75 | /** |
||||
76 | * @var Mandrill_Messages |
||||
77 | */ |
||||
78 | public $messages; |
||||
79 | /** |
||||
80 | * @var Mandrill_Whitelists |
||||
81 | */ |
||||
82 | public $whitelists; |
||||
83 | /** |
||||
84 | * @var Mandrill_Ips |
||||
85 | */ |
||||
86 | public $ips; |
||||
87 | /** |
||||
88 | * @var Mandrill_Internal |
||||
89 | */ |
||||
90 | public $internal; |
||||
91 | /** |
||||
92 | * @var Mandrill_Subaccounts |
||||
93 | */ |
||||
94 | public $subaccounts; |
||||
95 | /** |
||||
96 | * @var Mandrill_Urls |
||||
97 | */ |
||||
98 | public $urls; |
||||
99 | /** |
||||
100 | * @var Mandrill_Webhooks |
||||
101 | */ |
||||
102 | public $webhooks; |
||||
103 | /** |
||||
104 | * @var Mandrill_Senders |
||||
105 | */ |
||||
106 | public $senders; |
||||
107 | /** |
||||
108 | * @var Mandrill_Metadata |
||||
109 | */ |
||||
110 | public $metadata; |
||||
111 | /** |
||||
112 | * @var array |
||||
113 | */ |
||||
114 | public static $error_map = array( |
||||
115 | "ValidationError" => "Mandrill_ValidationError", |
||||
116 | "Invalid_Key" => "Mandrill_Invalid_Key", |
||||
117 | "PaymentRequired" => "Mandrill_PaymentRequired", |
||||
118 | "Unknown_Subaccount" => "Mandrill_Unknown_Subaccount", |
||||
119 | "Unknown_Template" => "Mandrill_Unknown_Template", |
||||
120 | "ServiceUnavailable" => "Mandrill_ServiceUnavailable", |
||||
121 | "Unknown_Message" => "Mandrill_Unknown_Message", |
||||
122 | "Invalid_Tag_Name" => "Mandrill_Invalid_Tag_Name", |
||||
123 | "Invalid_Reject" => "Mandrill_Invalid_Reject", |
||||
124 | "Unknown_Sender" => "Mandrill_Unknown_Sender", |
||||
125 | "Unknown_Url" => "Mandrill_Unknown_Url", |
||||
126 | "Unknown_TrackingDomain" => "Mandrill_Unknown_TrackingDomain", |
||||
127 | "Invalid_Template" => "Mandrill_Invalid_Template", |
||||
128 | "Unknown_Webhook" => "Mandrill_Unknown_Webhook", |
||||
129 | "Unknown_InboundDomain" => "Mandrill_Unknown_InboundDomain", |
||||
130 | "Unknown_InboundRoute" => "Mandrill_Unknown_InboundRoute", |
||||
131 | "Unknown_Export" => "Mandrill_Unknown_Export", |
||||
132 | "IP_ProvisionLimit" => "Mandrill_IP_ProvisionLimit", |
||||
133 | "Unknown_Pool" => "Mandrill_Unknown_Pool", |
||||
134 | "NoSendingHistory" => "Mandrill_NoSendingHistory", |
||||
135 | "PoorReputation" => "Mandrill_PoorReputation", |
||||
136 | "Unknown_IP" => "Mandrill_Unknown_IP", |
||||
137 | "Invalid_EmptyDefaultPool" => "Mandrill_Invalid_EmptyDefaultPool", |
||||
138 | "Invalid_DeleteDefaultPool" => "Mandrill_Invalid_DeleteDefaultPool", |
||||
139 | "Invalid_DeleteNonEmptyPool" => "Mandrill_Invalid_DeleteNonEmptyPool", |
||||
140 | "Invalid_CustomDNS" => "Mandrill_Invalid_CustomDNS", |
||||
141 | "Invalid_CustomDNSPending" => "Mandrill_Invalid_CustomDNSPending", |
||||
142 | "Metadata_FieldLimit" => "Mandrill_Metadata_FieldLimit", |
||||
143 | "Unknown_MetadataField" => "Mandrill_Unknown_MetadataField" |
||||
144 | ); |
||||
145 | |||||
146 | public function __construct($apikey = null) |
||||
147 | { |
||||
148 | if (!$apikey) { |
||||
149 | $apikey = getenv('MANDRILL_APIKEY'); |
||||
150 | } |
||||
151 | if (!$apikey) { |
||||
152 | $apikey = $this->readConfigs(); |
||||
153 | } |
||||
154 | if (!$apikey) { |
||||
155 | throw new Mandrill_Error('You must provide a Mandrill API key'); |
||||
156 | } |
||||
157 | $this->apikey = $apikey; |
||||
158 | |||||
159 | $this->ch = curl_init(); |
||||
160 | curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mandrill-PHP/1.0.54'); |
||||
161 | curl_setopt($this->ch, CURLOPT_POST, true); |
||||
162 | // We use our own follow, see function curl_exec_follow |
||||
163 | // curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true); |
||||
164 | curl_setopt($this->ch, CURLOPT_HEADER, false); |
||||
165 | curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); |
||||
166 | curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, 30); |
||||
167 | curl_setopt($this->ch, CURLOPT_TIMEOUT, 600); |
||||
168 | |||||
169 | // fix ca cert permissions |
||||
170 | if (strlen(ini_get('curl.cainfo')) === 0) { |
||||
171 | curl_setopt($this->ch, CURLOPT_CAINFO, CaBundle::getBundledCaBundlePath()); |
||||
172 | } |
||||
173 | |||||
174 | $this->root = rtrim($this->root, '/') . '/'; |
||||
175 | |||||
176 | $this->templates = new Mandrill_Templates($this); |
||||
177 | $this->exports = new Mandrill_Exports($this); |
||||
178 | $this->users = new Mandrill_Users($this); |
||||
179 | $this->rejects = new Mandrill_Rejects($this); |
||||
180 | $this->inbound = new Mandrill_Inbound($this); |
||||
181 | $this->tags = new Mandrill_Tags($this); |
||||
182 | $this->messages = new Mandrill_Messages($this); |
||||
183 | $this->whitelists = new Mandrill_Whitelists($this); |
||||
184 | $this->ips = new Mandrill_Ips($this); |
||||
185 | $this->internal = new Mandrill_Internal($this); |
||||
186 | $this->subaccounts = new Mandrill_Subaccounts($this); |
||||
187 | $this->urls = new Mandrill_Urls($this); |
||||
188 | $this->webhooks = new Mandrill_Webhooks($this); |
||||
189 | $this->senders = new Mandrill_Senders($this); |
||||
190 | $this->metadata = new Mandrill_Metadata($this); |
||||
191 | } |
||||
192 | |||||
193 | /** |
||||
194 | * A workaround for cURL: follow locations with safe_mode enabled or open_basedir set |
||||
195 | * |
||||
196 | * This method is used in the call method in Mandrill.php instead of the original curl_exec |
||||
197 | * |
||||
198 | * @link http://slopjong.de/2012/03/31/curl-follow-locations-with-safe_mode-enabled-or-open_basedir-set/ |
||||
199 | * @param resource|CurlHandle $ch |
||||
200 | * @param int $maxredirect |
||||
201 | * @return boolean |
||||
202 | */ |
||||
203 | public static function curl_exec_follow($ch, &$maxredirect = null) |
||||
204 | { |
||||
205 | $mr = $maxredirect === null ? 5 : intval($maxredirect); |
||||
206 | |||||
207 | if (ini_get('open_basedir') == '' && ini_get('safe_mode') == 'Off') { |
||||
208 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0); |
||||
209 | curl_setopt($ch, CURLOPT_MAXREDIRS, $mr); |
||||
210 | // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); |
||||
211 | } else { |
||||
212 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); |
||||
213 | |||||
214 | if ($mr > 0) { |
||||
215 | $original_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); |
||||
216 | $newurl = $original_url; |
||||
217 | |||||
218 | $rch = curl_copy_handle($ch); |
||||
219 | |||||
220 | curl_setopt($rch, CURLOPT_HEADER, true); |
||||
221 | curl_setopt($rch, CURLOPT_NOBODY, true); |
||||
222 | curl_setopt($rch, CURLOPT_FORBID_REUSE, false); |
||||
223 | do { |
||||
224 | curl_setopt($rch, CURLOPT_URL, $newurl); |
||||
225 | $header = curl_exec($rch); |
||||
226 | if (curl_errno($rch)) { |
||||
227 | $code = 0; |
||||
228 | } else { |
||||
229 | $code = curl_getinfo($rch, CURLINFO_HTTP_CODE); |
||||
230 | if ($code == 301 || $code == 302) { |
||||
231 | preg_match('/Location:(.*?)\n/', $header, $matches); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
232 | $newurl = trim(array_pop($matches)); |
||||
233 | |||||
234 | // if no scheme is present then the new url is a |
||||
235 | // relative path and thus needs some extra care |
||||
236 | if (!preg_match("/^https?:/i", $newurl)) { |
||||
237 | $newurl = $original_url . $newurl; |
||||
238 | } |
||||
239 | } else { |
||||
240 | $code = 0; |
||||
241 | } |
||||
242 | } |
||||
243 | } while ($code && --$mr); |
||||
244 | |||||
245 | curl_close($rch); |
||||
246 | |||||
247 | if (!$mr) { |
||||
248 | if ($maxredirect === null) { |
||||
249 | trigger_error('Too many redirects.', E_USER_WARNING); |
||||
250 | } else { |
||||
251 | $maxredirect = 0; |
||||
252 | } |
||||
253 | |||||
254 | return false; |
||||
255 | } |
||||
256 | curl_setopt($ch, CURLOPT_URL, $newurl); |
||||
257 | } |
||||
258 | } |
||||
259 | return curl_exec($ch); |
||||
0 ignored issues
–
show
|
|||||
260 | } |
||||
261 | |||||
262 | public function __destruct() |
||||
263 | { |
||||
264 | curl_close($this->ch); |
||||
265 | } |
||||
266 | |||||
267 | public function call($url, $params) |
||||
268 | { |
||||
269 | $params['key'] = $this->apikey; |
||||
270 | $params = json_encode($params); |
||||
271 | $ch = $this->ch; |
||||
272 | |||||
273 | curl_setopt($ch, CURLOPT_URL, $this->root . $url . '.json'); |
||||
274 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); |
||||
275 | curl_setopt($ch, CURLOPT_POSTFIELDS, $params); |
||||
276 | curl_setopt($ch, CURLOPT_VERBOSE, $this->debug); |
||||
277 | |||||
278 | $start = microtime(true); |
||||
279 | $this->log('Call to ' . $this->root . $url . '.json: ' . $params); |
||||
280 | if ($this->debug) { |
||||
281 | $curl_buffer = fopen('php://memory', 'w+'); |
||||
282 | curl_setopt($ch, CURLOPT_STDERR, $curl_buffer); |
||||
283 | } |
||||
284 | |||||
285 | // $response_body = curl_exec($ch); |
||||
286 | $response_body = self::curl_exec_follow($ch); |
||||
287 | $info = curl_getinfo($ch); |
||||
288 | $time = microtime(true) - $start; |
||||
289 | if ($this->debug) { |
||||
290 | rewind($curl_buffer); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
291 | $this->log(stream_get_contents($curl_buffer)); |
||||
292 | fclose($curl_buffer); |
||||
293 | } |
||||
294 | $this->log('Completed in ' . number_format($time * 1000, 2) . 'ms'); |
||||
295 | $this->log('Got response: ' . $response_body); |
||||
296 | |||||
297 | if (curl_error($ch)) { |
||||
298 | throw new Mandrill_HttpError("API call to $url failed: " . curl_error($ch)); |
||||
299 | } |
||||
300 | $result = json_decode($response_body, true); |
||||
0 ignored issues
–
show
$response_body of type boolean is incompatible with the type string expected by parameter $json of json_decode() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
301 | if ($result === null) { |
||||
302 | throw new Mandrill_Error('We were unable to decode the JSON response from the Mandrill API: ' . $response_body); |
||||
303 | } |
||||
304 | |||||
305 | if (floor($info['http_code'] / 100) >= 4) { |
||||
306 | throw $this->castError($result); |
||||
307 | } |
||||
308 | |||||
309 | return $result; |
||||
310 | } |
||||
311 | |||||
312 | public function readConfigs() |
||||
313 | { |
||||
314 | $paths = array('~/.mandrill.key', '/etc/mandrill.key'); |
||||
315 | foreach ($paths as $path) { |
||||
316 | if (file_exists($path)) { |
||||
317 | $apikey = trim(file_get_contents($path)); |
||||
318 | if ($apikey) { |
||||
319 | return $apikey; |
||||
320 | } |
||||
321 | } |
||||
322 | } |
||||
323 | return false; |
||||
324 | } |
||||
325 | |||||
326 | public function castError($result) |
||||
327 | { |
||||
328 | if ($result['status'] !== 'error' || !$result['name']) { |
||||
329 | throw new Mandrill_Error('We received an unexpected error: ' . json_encode($result)); |
||||
330 | } |
||||
331 | |||||
332 | $class = (isset(self::$error_map[$result['name']])) ? self::$error_map[$result['name']] : 'Mandrill_Error'; |
||||
333 | return new $class($result['message'], $result['code']); |
||||
334 | } |
||||
335 | |||||
336 | public function log($msg) |
||||
337 | { |
||||
338 | if ($this->debug) { |
||||
339 | error_log($msg); |
||||
340 | } |
||||
341 | } |
||||
342 | } |
||||
343 |