| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |