Source of file Postmark.php
Size: 17,448 Bytes - Last Modified: 2021-12-23T10:29:02+00:00
/var/www/docs.ssmods.com/process/src/code/thirdparty/postmark/Postmark.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 | <?php /** * Postmark PHP class * * Copyright 2011, Markus Hedlund, Mimmin AB, www.mimmin.com * Licensed under the MIT License. * Redistributions of files must retain the above copyright notice. * * @author Markus Hedlund (markus@mimmin.com) at mimmin (www.mimmin.com) * @copyright Copyright 2009 - 2011, Markus Hedlund, Mimmin AB, www.mimmin.com * @version 0.4.5 * @license http://www.opensource.org/licenses/mit-license.php The MIT License * * Usage: * Mail_Postmark::compose() * ->addTo('address@example.com', 'Name') * ->subject('Subject') * ->messagePlain('Plaintext message') * ->tag('Test tag') * ->send(); * * or: * * $email = new Mail_Postmark(); * $email->addTo('address@example.com', 'Name') * ->subject('Subject') * ->messagePlain('Plaintext message') * ->tag('Test tag') * ->send(); */ class Mail_Postmark { const DEBUG_OFF = 0; const DEBUG_VERBOSE = 1; const DEBUG_RETURN = 2; const TESTING_API_KEY = 'POSTMARK_API_TEST'; const MAX_ATTACHMENT_SIZE = 10485760; // 10 MB const RECIPIENT_TYPE_TO = 'to'; const RECIPIENT_TYPE_CC = 'cc'; const RECIPIENT_TYPE_BCC = 'bcc'; static $_mimeTypes = array('ai' => 'application/postscript', 'avi' => 'video/x-msvideo', 'doc' => 'application/msword', 'eps' => 'application/postscript', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'mov' => 'video/quicktime', 'mp3' => 'audio/mpeg', 'mpg' => 'video/mpeg', 'pdf' => 'application/pdf', 'ppt' => 'application/vnd.ms-powerpoint', 'ps' => 'application/postscript', 'rtf' => 'application/rtf', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'txt' => 'text/plain', 'xls' => 'application/vnd.ms-excel', 'csv' => 'text/comma-separated-values', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'flv' => 'video/x-flv', 'ics' => 'text/calendar', 'log' => 'text/plain', 'png' => 'image/png', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'psd' => 'image/photoshop', 'rm' => 'application/vnd.rn-realmedia', 'swf' => 'application/x-shockwave-flash', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xml' => 'text/xml'); private $_apiKey; private $_from; private $_to = array(); private $_cc = array(); private $_bcc = array(); private $_replyTo; private $_subject; private $_tag; private $_messagePlain; private $_messageHtml; private $_headers = array(); private $_attachments = array(); private $_debugMode = self::DEBUG_OFF; /** * Initialize */ public function __construct() { if (class_exists('Mail_Postmark_Adapter', false)) { require_once('Adapter_Interface.php'); $reflection = new ReflectionClass('Mail_Postmark_Adapter'); if (!$reflection->implementsInterface('Mail_Postmark_Adapter_Interface')) { trigger_error('Mail_Postmark_Adapter must implement interface Mail_Postmark_Adapter_Interface', E_USER_ERROR); } $this->_apiKey = Mail_Postmark_Adapter::getApiKey(); Mail_Postmark_Adapter::setupDefaults($this); } else { if (!defined('POSTMARKAPP_API_KEY')) { trigger_error('Postmark API key is not set', E_USER_ERROR); } $this->_apiKey = POSTMARKAPP_API_KEY; if (defined('POSTMARKAPP_MAIL_FROM_ADDRESS')) { $this->from( POSTMARKAPP_MAIL_FROM_ADDRESS, defined('POSTMARKAPP_MAIL_FROM_NAME') ? POSTMARKAPP_MAIL_FROM_NAME : null ); } } $this->messageHtml(null)->messagePlain(null); } /** * Add a physical file as an attachment * Options: * - filenameAlias, use a different filename for the attachment * * @param string $filename Location of the file * @param array $options An optional array with options * @throws InvalidArgumentException If file doesn't exist * @throws OverflowException If maximum attachment size has been reached * @return Mail_Postmark */ public function &addAttachment($filename, $options = array()) { if (!is_file($filename)) { throw new InvalidArgumentException("File \"{$filename}\" does not exist"); } $this->addCustomAttachment( isset($options['filenameAlias']) ? $options['filenameAlias'] : basename($filename), file_get_contents($filename), $this->_getMimeType($filename) ); return $this; } /** * Add a BCC address * * @param string $address E-mail address used in BCC * @param string $name Optional. Name used in BCC * @throws InvalidArgumentException On invalid address * @throws OverflowException If there are too many email recipients * @return Mail_Postmark */ public function &addBcc($address, $name = null) { $this->_addRecipient(self::RECIPIENT_TYPE_BCC, $address, $name); return $this; } /** * Add a CC address * * @param string $address E-mail address used in CC * @param string $name Optional. Name used in CC * @throws InvalidArgumentException On invalid address * @throws OverflowException If there are too many email recipients * @return Mail_Postmark */ public function &addCc($address, $name = null) { $this->_addRecipient(self::RECIPIENT_TYPE_CC, $address, $name); return $this; } /** * Add an attachment. * * @param string $filename What to call the file * @param string $content Raw file data * @param string $mimeType The mime type of the file * @throws OverflowException If maximum attachment size has been reached * @return Mail_Postmark */ public function &addCustomAttachment($filename, $content, $mimeType) { $length = strlen($content); $lengthSum = 0; foreach ($this->_attachments as $file) { $lengthSum += $file['length']; } if ($lengthSum + $length > self::MAX_ATTACHMENT_SIZE) { throw new OverflowException("Maximum attachment size reached"); } $this->_attachments[$filename] = array( 'content' => base64_encode($content), 'mimeType' => $mimeType, 'length' => $length ); return $this; } /** * Add a custom header * * @param string $name Custom header name * @param string $value Custom header value * @return Mail_Postmark */ public function &addHeader($name, $value) { $this->_headers[$name] = $value; return $this; } /** * Add a receiver * * @param string $address E-mail address used in To * @param string $name Optional. Name used in To * @throws InvalidArgumentException On invalid address * @throws OverflowException If there are too many email recipients * @return Mail_Postmark */ public function &addTo($address, $name = null) { $this->_addRecipient(self::RECIPIENT_TYPE_TO, $address, $name); return $this; } /** * New e-mail * * @return Mail_Postmark */ public static function compose() { return new self(); } /** * Turns debug output on * * @param int $mode One of the debug constants * @return Mail_Postmark */ public function &debug($mode = self::DEBUG_VERBOSE) { $this->_debugMode = $mode; return $this; } /** * Specify sender. Overwrites default From. Note that the address * must first be added in the Postmark app admin interface * * @param string $address E-mail address used in From * @param string $name Optional. Name used in From * @throws InvalidArgumentException On invalid address * @return Mail_Postmark */ public function &from($address, $name = null) { if (!$this->_validateAddress($address)) { throw new InvalidArgumentException("From address \"{$address}\" is invalid"); } $this->_from = array('address' => $address, 'name' => $name); return $this; } /** * Specify sender name. Overwrites default From name, but doesn't change address. * * @param string $name Name used in From * @return Mail_Postmark */ public function &fromName($name) { $this->_from['name'] = $name; return $this; } /** * Add HTML message. Can be used in conjunction with messagePlain() * * @param string $message E-mail message * @return Mail_Postmark */ public function &messageHtml($message) { $this->_messageHtml = $message; return $this; } /** * Add plaintext message. Can be used in conjunction with messageHtml() * @param string $message E-mail message * @return Mail_Postmark */ public function &messagePlain($message) { $this->_messagePlain = $message; return $this; } /** * Specify reply-to * * @param string $address E-mail address used in To * @param string $name Optional. Name used in To * @throws InvalidArgumentException On invalid address * @return Mail_Postmark */ public function &replyTo($address, $name = null) { if (!$this->_validateAddress($address)) { throw new InvalidArgumentException("Reply To address \"{$address}\" is invalid"); } $this->_replyTo = array('address' => $address, 'name' => $name); return $this; } /** * Sends the e-mail. Prints debug output if debug mode is turned on * * Options: * returnMessageId * * @throws Exception If HTTP code 422, Exception with API error code and Postmark message, otherwise HTTP code. * @throws BadMethodCallException If From address, To address or Subject is missing * @return boolean - True if success and $returnID is false. * @return string - if $returnID is true and one message is sent. * @return array - if DEBUG_RETURN is enabled. */ public function send($options = array()) { $this->_validateData(); $data = $this->_prepareData(); $headers = array( 'Accept: application/json', 'Content-Type: application/json', 'X-Postmark-Server-Token: ' . $this->_apiKey ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.postmarkapp.com/email'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/Certificate/cacert.pem'); $return = curl_exec($ch); $curlError = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $this->_log(array( 'messageData' => $data, 'return' => $return, 'curlError' => $curlError, 'httpCode' => $httpCode )); if (($this->_debugMode & self::DEBUG_VERBOSE) === self::DEBUG_VERBOSE) { echo "JSON: " . json_encode($data) . "\nHeaders: \n\t" . implode("\n\t", $headers) . "\nReturn:\n{$return}" . "\nCurl error: {$curlError}" . "\nHTTP code: {$httpCode}"; } if ($curlError !== '') { throw new Exception($curlError); } if (!$this->_isTwoHundred($httpCode)) { if ($httpCode == 422) { $return = json_decode($return); throw new Exception($return->Message, $return->ErrorCode); } else { throw new Exception("Error while mailing. Postmark returned HTTP code {$httpCode} with message \"{$return}\"", $httpCode); } } if (($this->_debugMode & self::DEBUG_RETURN) === self::DEBUG_RETURN) { return array( 'json' => json_encode($data), 'headers' => $headers, 'return' => $return, 'curlError' => $curlError, 'httpCode' => $httpCode ); } // Return the ID of the message sent if the option is set. if(!empty($options['returnMessageId'])) { $messageInformation = json_decode($return); return $messageInformation->MessageID; } return true; } /** * Specify subject * * @param string $subject E-mail subject * @return Mail_Postmark */ public function &subject($subject) { $this->_subject = $subject; return $this; } /** * You can categorize outgoing email using the optional Tag property. * If you use different tags for the different types of emails your * application generates, you will be able to get detailed statistics * for them through the Postmark user interface. * Only 1 tag per email is supported. * * @param string $tag One tag * @return Mail_Postmark */ public function &tag($tag) { $this->_tag = $tag; return $this; } /** * Specify receiver. Use addTo to add more. * * @deprecated Use addTo. * @param string $address E-mail address used in To * @param string $name Optional. Name used in To * @return Mail_Postmark */ public function &to($address, $name = null) { $this->_to = array(); $this->addTo($address, $name); return $this; } /** * @param string $type Either 'to', 'cc' or 'bcc' * @param string $address * @param string|null $name * @throws InvalidArgumentException On invalid address * @throws OverflowException If there are too many email recipients */ public function _addRecipient($type, $address, $name = null) { $address = trim($address); if (!$this->_validateAddress($address)) { throw new InvalidArgumentException("Address \"{$address}\" is invalid"); } if (count($this->_to) + count($this->_cc) + count($this->_bcc) === 20) { throw new OverflowException('Too many email recipients'); } $data = array('address' => $address, 'name' => $name); switch ($type) { case self::RECIPIENT_TYPE_TO: $this->_to[] = $data; break; case self::RECIPIENT_TYPE_CC: $this->_cc[] = $data; break; case self::RECIPIENT_TYPE_BCC: $this->_bcc[] = $data; break; } } private function _createAddress($address, $name = null) { if (isset($name)) { return '"' . str_replace('"', '', $name) . '" <' . $address . '>'; } else { return $address; } } /** * Try to detect the mime type * @param $filename * @return string */ private function _getMimeType($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); if (isset(self::$_mimeTypes[$extension])) { return self::$_mimeTypes[$extension]; } else if (function_exists('mime_content_type')) { return mime_content_type($filename); } else if (function_exists('finfo_file')) { $fh = finfo_open(FILEINFO_MIME); $mime = finfo_file($fh, $filename); finfo_close($fh); return $mime; } else if ($image = getimagesize($filename)) { return $image[2]; } else { return 'application/octet-stream'; } } /** * If a number is 200-299 * @param $value * @return bool */ private function _isTwoHundred($value) { return intval($value / 100) == 2; } /** * Call the logger method, if one exists * * @param array $logData */ private function _log($logData) { if (class_exists('Mail_Postmark_Adapter', false)) { Mail_Postmark_Adapter::log($logData); } } /** * Prepares the data array * @return array */ private function _prepareData() { $data = array( 'Subject' => $this->_subject ); $data['From'] = $this->_createAddress($this->_from['address'], $this->_from['name']); $data['To'] = array(); $data['Cc'] = array(); $data['Bcc'] = array(); foreach ($this->_to as $to) { $data['To'][] = $this->_createAddress($to['address'], $to['name']); } foreach ($this->_cc as $cc) { $data['Cc'][] = $this->_createAddress($cc['address'], $cc['name']); } foreach ($this->_bcc as $bcc) { $data['Bcc'][] = $this->_createAddress($bcc['address'], $bcc['name']); } $data['To'] = implode(', ', $data['To']); if (empty($data['Cc'])) { unset($data['Cc']); } else { $data['Cc'] = implode(', ', $data['Cc']); } if (empty($data['Bcc'])) { unset($data['Bcc']); } else { $data['Bcc'] = implode(', ', $data['Bcc']); } if ($this->_replyTo !== null) { $data['ReplyTo'] = $this->_createAddress($this->_replyTo['address'], $this->_replyTo['name']); } if ($this->_messageHtml !== null) { $data['HtmlBody'] = $this->_messageHtml; } if ($this->_messagePlain !== null) { $data['TextBody'] = $this->_messagePlain; } if ($this->_tag !== null) { $data['Tag'] = $this->_tag; } if (!empty($this->_headers)) { $data['Headers'] = array(); foreach ($this->_headers as $name => $value) { $data['Headers'][] = array('Name' => $name, 'Value' => $value); } } if (!empty($this->_attachments)) { $data['Attachments'] = array(); foreach ($this->_attachments as $filename => $file) { $data['Attachments'][] = array( 'Name' => $filename, 'Content' => $file['content'], 'ContentType' => $file['mimeType'] ); } } return $data; } /** * Validates an e-mail address * @param $email * @return bool */ private function _validateAddress($email) { // http://php.net/manual/en/function.filter-var.php // return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; // filter_var proved to be unworthy (passed foo..bar@domain.com as valid), // and was therefore replace with $regex = "/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i"; // from http://fightingforalostcause.net/misc/2006/compare-email-regex.php return preg_match($regex, $email) === 1; } /** * Validate that the email can be sent * * @throws BadMethodCallException If From address, To address or Subject is missing */ private function _validateData() { if ($this->_from['address'] === null) { throw new BadMethodCallException('From address is not set'); } if (empty($this->_to)) { throw new BadMethodCallException('No To address is set'); } if (!isset($this->_subject)) { throw new BadMethodCallException('Subject is not set'); } } } |