diff --git a/sites/all/libraries/mailgun/.gitignore b/sites/all/libraries/mailgun/.gitignore new file mode 100644 index 00000000..dc48fa8e --- /dev/null +++ b/sites/all/libraries/mailgun/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +vendor +composer.phar +smoketest.php +rackspace_logo.jpg +mailgun_icon.png +build +composer.lock +nbproject/* +.idea diff --git a/sites/all/libraries/mailgun/.travis.yml b/sites/all/libraries/mailgun/.travis.yml new file mode 100644 index 00000000..fadbf8cb --- /dev/null +++ b/sites/all/libraries/mailgun/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 5.5 + - 5.6 + - 7.0 + - hhvm + +before_install: + - travis_retry composer self-update + +install: + - travis_retry composer install + +script: + - phpunit \ No newline at end of file diff --git a/sites/all/libraries/mailgun/CHANGELOG.md b/sites/all/libraries/mailgun/CHANGELOG.md new file mode 100644 index 00000000..550fd769 --- /dev/null +++ b/sites/all/libraries/mailgun/CHANGELOG.md @@ -0,0 +1,47 @@ +## 1.7 (2014-1-30) + +Bugfixes: + - patched bug for attachments related to duplicate aggregator bug in Guzzle (#32 @travelton) + +## 1.6 (2014-1-13) + +Enhancement: + - adjust file attachment/inline name (#21 @travelton) + +Bugfixes: + - fixed issue with unordered route actions (#23 @travelton) + +## 1.5 (2013-12-13) + +Enhancement: + - added ability to define non-https endpoint for debugging purposes (#23 @travelton) + +## 1.4 (2013-10-16) + +Bugfixes: + - template IDs were missing from recipient-variables (#15 @travelton) + - batch jobs trigger on to, cc, and bcc (#18 @travelton) + - batch jobs include recipient-variables for to, cc, and bcc (#18 @travelton) + - added method to return message-ids, for easier access (#19 @travelton) + +## 1.3 (2013-09-12) + +Bugfixes: + + - relaxed Guzzle requirement (#7 @travelton) + - fixed reply-to bug (#9 @travelton) + +## 1.2 (2013-09-05) + +Bugfixes: + + - fixed exception handling constants (@travelton) + - fixed MessageBuilder $baseAddress return (#1 @yoye) + - adjusted scope of recipient-variables (#3 @yoye) + - fixed misspellings of Exceptions (#2 @dboggus) + - undefined DEFAULT_TIME_ZONE (#4 @yoye) + - added message IDs to return for BatchMessage (@travelton) + +## 1.1 (2013-08-21) + +Initial Release! diff --git a/sites/all/libraries/mailgun/README.md b/sites/all/libraries/mailgun/README.md new file mode 100644 index 00000000..52955dc1 --- /dev/null +++ b/sites/all/libraries/mailgun/README.md @@ -0,0 +1,211 @@ +Mailgun-PHP +=========== + +This is the Mailgun PHP SDK. This SDK contains methods for easily interacting +with the Mailgun API. +Below are examples to get you started. For additional examples, please see our +official documentation +at http://documentation.mailgun.com + +[![Latest Stable Version](https://poser.pugx.org/mailgun/mailgun-php/v/stable.png)](https://packagist.org/packages/mailgun/mailgun-php) +[![Build Status](https://travis-ci.org/mailgun/mailgun-php.png)](https://travis-ci.org/mailgun/mailgun-php) + +Installation +------------ +To install the SDK, you will need to be using [Composer](http://getcomposer.org/) +in your project. +If you aren't using Composer yet, it's really simple! Here's how to install +composer and the Mailgun SDK. + +```PHP +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Mailgun as a dependency +php composer.phar require mailgun/mailgun-php:~2.0 +``` + +You do also need to choose what library to use when you are sending http messages. Consult the +[php-http/client-implementation](https://packagist.org/providers/php-http/client-implementation) virtual package to +find adapters to use. For more information about virtual packages please refer to +[Httplug](http://docs.httplug.io/en/latest/virtual-package/). Example: + +```bash +php composer.phar require php-http/guzzle6-adapter:^1.0 +``` + +When creating a new `Mailgun` object you must provide an instance of the `HttpClient`. + +```php +$client = new \Http\Adapter\Guzzle6\Client(); +$mailgun = new \Mailgun\Mailgun('api_key', $client); +``` + +You could also rely on the [auto discovery feature of Httplug](http://docs.php-http.org/en/latest/discovery.html). This +means that you need to install `puli/composer-plugin` and put a puli.phar in your project root. + + +**For shared hosts without SSH access, check out our [Shared Host Instructions](SharedHostInstall.md).** + +**Rather just download the files? [Library Download](https://9f67cbbd1116d8afb399-7760483f5d1e5f28c2d253278a2a5045.ssl.cf2.rackcdn.com/mailgun-php-1.7.2.zip).** + +Next, require Composer's autoloader, in your application, to automatically +load the Mailgun SDK in your project: +```PHP +require 'vendor/autoload.php'; +use Mailgun\Mailgun; +``` + +Usage +----- +Here's how to send a message using the SDK: + +```php +# First, instantiate the SDK with your API credentials and define your domain. +$mg = new Mailgun("key-example"); +$domain = "example.com"; + +# Now, compose and send your message. +$mg->sendMessage($domain, array('from' => 'bob@example.com', + 'to' => 'sally@example.com', + 'subject' => 'The PHP SDK is awesome!', + 'text' => 'It is so simple to send a message.')); +``` + +Or obtain the last 25 log items: +```php +# First, instantiate the SDK with your API credentials and define your domain. +$mg = new Mailgun("key-example"); +$domain = "example.com"; + +# Now, issue a GET against the Logs endpoint. +$mg->get("$domain/log", array('limit' => 25, + 'skip' => 0)); +``` + +Response +-------- + +The results, provided by the endpoint, are returned as an object, which you +can traverse like an array. + +Example: + +```php +$mg = new Mailgun("key-example"); +$domain = "example.com"; + +$result = $mg->get("$domain/log", array('limit' => 25, + 'skip' => 0)); + +$httpResponseCode = $result->http_response_code; +$httpResponseBody = $result->http_response_body; + +# Iterate through the results and echo the message IDs. +$logItems = $result->http_response_body->items; +foreach($logItems as $logItem){ + echo $logItem->message_id . "\n"; +} +``` + +Example Contents: +**$httpResponseCode** will contain an integer. You can find how we use HTTP response +codes in our documentation: +http://documentation.mailgun.com/api-intro.html?highlight=401#errors + +**$httpResponseBody** will contain an object of the API response. In the above +example, a var_dump($result) would contain the following: + +``` +object(stdClass)#26 (2) { +["http_response_body"]=> + object(stdClass)#26 (2) { + ["total_count"]=> + int(12) + ["items"]=> + array(1) { + [0]=> + object(stdClass)#31 (5) { + ["hap"]=> + string(9) "delivered" + ["created_at"]=> + string(29) "Tue, 20 Aug 2013 20:24:34 GMT" + ["message"]=> + string(66) "Delivered: me@samples.mailgun.org → travis@mailgunhq.com 'Hello'" + ["type"]=> + string(4) "info" + ["message_id"]=> + string(46) "20130820202406.24739.21973@samples.mailgun.org" + } + } + } +} +``` + +Debugging +--------- + +Debugging the PHP SDK can be really helpful when things aren't working quite right. +To debug the SDK, here are some suggestions: + +Set the endpoint to Mailgun's Postbin. A Postbin is a web service that allows you to +post data, which is then displayed through a browser. This allows you to quickly determine +what is actually being transmitted to Mailgun's API. + +**Step 1 - Create a new Postbin.** +Go to http://bin.mailgun.net. The Postbin will generate a special URL. Save that URL. + +**Step 2 - Instantiate the Mailgun client using Postbin.** + +*Tip: The bin id will be the URL part after bin.mailgun.net. It will be random generated letters and numbers. For example, the bin id in this URL, http://bin.mailgun.net/aecf68de, is "aecf68de".* + +```php +# First, instantiate the SDK with your API credentials and define your domain. +$mg = new Mailgun('key-example', null, 'bin.mailgun.net'); +$mg->setApiVersion('aecf68de'); +$mg->setSslEnabled('false'); +$domain = 'example.com'; + +# Now, compose and send your message. +$mg->sendMessage($domain, array('from' => 'bob@example.com', + 'to' => 'sally@example.com', + 'subject' => 'The PHP SDK is awesome!', + 'text' => 'It is so simple to send a message.')); +``` +Additional Info +--------------- + +For usage examples on each API endpoint, head over to our official documentation +pages. + +This SDK includes a [Message Builder](src/Mailgun/Messages/README.md), +[Batch Message](src/Mailgun/Messages/README.md) and [Opt-In Handler](src/Mailgun/Lists/README.md) component. + +Message Builder allows you to quickly create the array of parameters, required +to send a message, by calling a methods for each parameter. +Batch Message is an extension of Message Builder, and allows you to easily send +a batch message job within a few seconds. The complexity of +batch messaging is eliminated! + +Framework integration +--------------------- + +If you are using a framework you might consider these composer packages to make the framework integration easier. + +* [tehplague/swiftmailer-mailgun-bundle](https://github.com/tehplague/swiftmailer-mailgun-bundle) for Symfony2 +* [Bogardo/Mailgun](https://github.com/Bogardo/Mailgun) for Laravel 4 +* [katanyoo/yii2-mailgun-mailer](https://github.com/katanyoo/yii2-mailgun-mailer) for Yii2 + +Support and Feedback +-------------------- + +Be sure to visit the Mailgun official +[documentation website](http://documentation.mailgun.com/) for additional +information about our API. + +If you find a bug, please submit the issue in Github directly. +[Mailgun-PHP Issues](https://github.com/mailgun/Mailgun-PHP/issues) + +As always, if you need additional assistance, drop us a note through your Control Panel at +[https://mailgun.com/cp/support](https://mailgun.com/cp/support). + diff --git a/sites/all/libraries/mailgun/SharedHostInstall.md b/sites/all/libraries/mailgun/SharedHostInstall.md new file mode 100644 index 00000000..bc94c0f1 --- /dev/null +++ b/sites/all/libraries/mailgun/SharedHostInstall.md @@ -0,0 +1,39 @@ +Shared Host Installation +======================== + +If you do not have SSH access to your server, fear not! You can still run +composer and download the SDK. Here's how... + +Installation +------------ + +Linux / Mac OSX: +*PHP is typically installed by default, consult your distribution documentation. Instructions from [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).* + +1. curl -sS https://getcomposer.org/installer | php +2. php composer.phar require mailgun/mailgun-php:~1.7.2 +3. The files will be downloaded to your local computer. +4. Upload the files to your webserver. + + +Windows: +*PHP must be installed on your computer, [download](http://windows.php.net/download/0). Instructions from [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-windows).* + +1. Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). +2. Open a Command Prompt and type "php composer require mailgun/mailgun-php:~1.7.2". +3. The files will be downloaded to your local computer. +4. Upload the files to your webserver. + + +Support and Feedback +-------------------- + +Be sure to visit the Mailgun official +[documentation website](http://documentation.mailgun.com/) for additional +information about our API. + +If you find a bug, please submit the issue in Github directly. +[Mailgun-PHP Issues](https://github.com/mailgun/Mailgun-PHP/issues) + +As always, if you need additional assistance, drop us a note at +[support@mailgun.com](mailto:support@mailgun.com). diff --git a/sites/all/libraries/mailgun/composer.json b/sites/all/libraries/mailgun/composer.json new file mode 100644 index 00000000..a7035051 --- /dev/null +++ b/sites/all/libraries/mailgun/composer.json @@ -0,0 +1,28 @@ +{ + "name": "mailgun/mailgun-php", + "description": "The Mailgun SDK provides methods for all API functions.", + "require": { + "php": "^5.5|^7.0", + "guzzlehttp/psr7": "~1.2", + "php-http/httplug": "^1.0", + "php-http/discovery": "^0.8" + }, + "require-dev": { + "phpunit/phpunit": "~4.6", + "php-http/guzzle6-adapter": "^1.0" + }, + "autoload": { + "psr-0": { + "Mailgun\\Tests": "tests/", + "Mailgun": "src/" + } + }, + "license": "MIT", + "authors": [ + { + "name": "Travis Swientek", + "email": "travis@mailgunhq.com" + } + ], + "minimum-stability": "stable" +} diff --git a/sites/all/libraries/mailgun/phpunit.xml.dist b/sites/all/libraries/mailgun/phpunit.xml.dist new file mode 100644 index 00000000..33cea162 --- /dev/null +++ b/sites/all/libraries/mailgun/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + + tests/Mailgun/Tests + + + + + diff --git a/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/GenericHTTPError.php b/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/GenericHTTPError.php new file mode 100644 index 00000000..b3706494 --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/GenericHTTPError.php @@ -0,0 +1,25 @@ +httpResponseCode = $response_code; + $this->httpResponseBody = $response_body; + } + + public function getHttpResponseCode() { + return $this->httpResponseCode; + } + + public function getHttpResponseBody() { + return $this->httpResponseBody; + } +} + +?> diff --git a/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/InvalidCredentials.php b/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/InvalidCredentials.php new file mode 100644 index 00000000..bfcc743b --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Connection/Exceptions/InvalidCredentials.php @@ -0,0 +1,4 @@ +apiKey = $apiKey; + $this->apiHost = $apiHost; + $this->httpClient = $httpClient; + } + + /** + * @param string $method + * @param string $uri + * @param array $body + * @param array $files + * @param array $headers + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + protected function send($method, $uri, $body = null, $files = [], array $headers = []) + { + $headers['User-Agent'] = Api::SDK_USER_AGENT.'/'.Api::SDK_VERSION; + $headers['Authorization'] = 'Basic '.base64_encode(sprintf('%s:%s', Api::API_USER, $this->apiKey)); + + if (!empty($files)) { + $body = new MultipartStream($files); + $headers['Content-Type'] = 'multipart/form-data; boundary='.$body->getBoundary(); + } + + $request = new Request($method, $this->getApiUrl($uri), $headers, $body); + $response = $this->getHttpClient()->sendRequest($request); + + return $this->responseHandler($response); + } + + /** + * @param string $endpointUrl + * @param array $postData + * @param array $files + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + public function post($endpointUrl, $postData = array(), $files = array()) + { + $postFiles = []; + + $fields = ['message', 'attachment', 'inline']; + foreach ($fields as $fieldName) { + if (isset($files[$fieldName])) { + if (is_array($files[$fieldName])) { + foreach ($files[$fieldName] as $file) { + $postFiles[] = $this->prepareFile($fieldName, $file); + } + } else { + $postFiles[] = $this->prepareFile($fieldName, $files[$fieldName]); + } + } + } + + $postDataMultipart = []; + foreach ($postData as $key => $value) { + if (is_array($value)) { + foreach ($value as $subValue) { + $postDataMultipart[] = [ + 'name' => $key, + 'contents' => $subValue, + ]; + } + } else { + $postDataMultipart[] = [ + 'name' => $key, + 'contents' => $value, + ]; + } + } + + return $this->send('POST', $endpointUrl, [], array_merge($postDataMultipart, $postFiles)); + } + + /** + * @param string $endpointUrl + * @param array $queryString + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + public function get($endpointUrl, $queryString = array()) + { + return $this->send('GET', $endpointUrl.'?'.http_build_query($queryString)); + } + + /** + * @param string $endpointUrl + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + public function delete($endpointUrl) + { + return $this->send('DELETE', $endpointUrl); + } + + /** + * @param string $endpointUrl + * @param array $putData + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + public function put($endpointUrl, $putData) + { + return $this->send('PUT', $endpointUrl, $putData); + } + + /** + * @param ResponseInterface $responseObj + * + * @return \stdClass + * + * @throws GenericHTTPError + * @throws InvalidCredentials + * @throws MissingEndpoint + * @throws MissingRequiredParameters + */ + public function responseHandler(ResponseInterface $responseObj) + { + $httpResponseCode = $responseObj->getStatusCode(); + if ($httpResponseCode === 200) { + $data = (string) $responseObj->getBody(); + $jsonResponseData = json_decode($data, false); + $result = new \stdClass(); + // return response data as json if possible, raw if not + $result->http_response_body = $data && $jsonResponseData === null ? $data : $jsonResponseData; + } elseif ($httpResponseCode == 400) { + throw new MissingRequiredParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_PARAMETERS.$this->getResponseExceptionMessage($responseObj)); + } elseif ($httpResponseCode == 401) { + throw new InvalidCredentials(ExceptionMessages::EXCEPTION_INVALID_CREDENTIALS); + } elseif ($httpResponseCode == 404) { + throw new MissingEndpoint(ExceptionMessages::EXCEPTION_MISSING_ENDPOINT.$this->getResponseExceptionMessage($responseObj)); + } else { + throw new GenericHTTPError(ExceptionMessages::EXCEPTION_GENERIC_HTTP_ERROR, $httpResponseCode, $responseObj->getBody()); + } + $result->http_response_code = $httpResponseCode; + + return $result; + } + + /** + * @param ResponseInterface $responseObj + * + * @return string + */ + protected function getResponseExceptionMessage(ResponseInterface $responseObj) + { + $body = (string) $responseObj->getBody(); + $response = json_decode($body); + if (json_last_error() == JSON_ERROR_NONE && isset($response->message)) { + return ' '.$response->message; + } + } + + /** + * Prepare a file for the postBody. + * + * @param string $fieldName + * @param string|array $filePath + */ + protected function prepareFile($fieldName, $filePath) + { + $filename = null; + // Backward compatibility code + if (is_array($filePath)) { + $filename = $filePath['remoteName']; + $filePath = $filePath['filePath']; + } + + // Remove leading @ symbol + if (strpos($filePath, '@') === 0) { + $filePath = substr($filePath, 1); + } + + return [ + 'name' => $fieldName, + 'contents' => fopen($filePath, 'r'), + 'filename' => $filename, + ]; + } + + + /** + * + * @return HttpClient + */ + protected function getHttpClient() + { + if ($this->httpClient === null) { + $this->httpClient = HttpClientDiscovery::find(); + } + + return $this->httpClient; + } + + /** + * @param $uri + * + * @return string + */ + private function getApiUrl($uri) + { + return $this->generateEndpoint($this->apiHost, $this->apiVersion, $this->sslEnabled).$uri; + } + + + /** + * @param string $apiEndpoint + * @param string $apiVersion + * @param bool $ssl + * + * @return string + */ + private function generateEndpoint($apiEndpoint, $apiVersion, $ssl) + { + if (!$ssl) { + return 'http://'.$apiEndpoint.'/'.$apiVersion.'/'; + } else { + return 'https://'.$apiEndpoint.'/'.$apiVersion.'/'; + } + } + + /** + * @param string $apiVersion + * + * @return RestClient + */ + public function setApiVersion($apiVersion) + { + $this->apiVersion = $apiVersion; + + return $this; + } + + /** + * @param boolean $sslEnabled + * + * @return RestClient + */ + public function setSslEnabled($sslEnabled) + { + $this->sslEnabled = $sslEnabled; + + return $this; + } +} diff --git a/sites/all/libraries/mailgun/src/Mailgun/Constants/Api.php b/sites/all/libraries/mailgun/src/Mailgun/Constants/Api.php new file mode 100644 index 00000000..7a2908ef --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Constants/Api.php @@ -0,0 +1,16 @@ + $recipientAddress, 'l' => $mailingList); + $encodedInnerPayload = base64_encode(json_encode($innerPayload)); + + $innerHash = hash_hmac("sha1", $encodedInnerPayload, $secretAppId); + $outerPayload = array('h' => $innerHash, 'p' => $encodedInnerPayload); + + return urlencode(base64_encode(json_encode($outerPayload))); + } + + /** + * @param string $secretAppId + * @param string $uniqueHash + * @return array|bool + */ + public function validateHash($secretAppId, $uniqueHash){ + $decodedOuterPayload = json_decode(base64_decode(urldecode($uniqueHash)), true); + + $decodedHash = $decodedOuterPayload['h']; + $innerPayload = $decodedOuterPayload['p']; + + $decodedInnerPayload = json_decode(base64_decode($innerPayload), true); + $computedInnerHash = hash_hmac("sha1", $innerPayload, $secretAppId); + + if($computedInnerHash == $decodedHash){ + return array('recipientAddress' => $decodedInnerPayload['r'], 'mailingList' => $decodedInnerPayload['l']); + } + + return false; + } +} diff --git a/sites/all/libraries/mailgun/src/Mailgun/Lists/README.md b/sites/all/libraries/mailgun/src/Mailgun/Lists/README.md new file mode 100644 index 00000000..940e4be7 --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Lists/README.md @@ -0,0 +1,116 @@ +Mailgun - Lists +==================== + +This is the Mailgun PHP *Lists* utilities. + +The below assumes you've already installed the Mailgun PHP SDK in to your project. +If not, go back to the master README for instructions. + +There is currently one utility provided. + +OptInHandler: Provides methods for authenticating an OptInRequest. + +The typical flow for using this utility would be as follows: +**Recipient Requests Subscribe** -> [Validate Recipient Address] -> [Generate Opt In Link] -> [Email Recipient Opt In Link] +**Recipient Clicks Opt In Link** -> [Validate Opt In Link] -> [Subscribe User] -> [Send final confirmation] + +The above flow is modeled below. + +Usage - Opt-In Handler (Recipient Requests Subscribe) +----------------------------------------------------- +Here's how to use Opt-In Handler to validate Opt-In requests. + +```php +# First, instantiate the SDK with your API credentials, domain, and required parameters for example. +$mg = new Mailgun('key-example'); +$mgValidate = new Mailgun('pub-key-example'); + +$domain = 'example.com'; +$mailingList = 'youlist@example.com'; +$secretPassphrase = 'a_secret_passphrase'; +$recipientAddress = 'recipient@example.com'; + +# Let's validate the customer's email address, using Mailgun's validation endpoint. +$result = $mgValidate->get('address/validate', array('address' => $recipientAddress)); + +if($result->http_response_body->is_valid == true){ + # Next, instantiate an OptInHandler object from the SDK. + $optInHandler = $mg->OptInHandler(); + + # Next, generate a hash. + $generatedHash = $optInHandler->generateHash($mailingList, $secretPassphrase, $recipientAddress); + + # Now, let's send a confirmation to the recipient with our link. + $mg->sendMessage($domain, array('from' => 'bob@example.com', + 'to' => $recipientAddress, + 'subject' => 'Please Confirm!', + 'html' => "Hello,

You have requested to be subscribed + to the mailing list $mailingList. Please + confirm your subscription.

Thank you!")); + + # Finally, let's add the subscriber to a Mailing List, as unsubscribed, so we can track non-conversions. + $mg->post("lists/$mailingList/members", array('address' => $recipientAddress, + 'subscribed' => 'no', + 'upsert' => 'yes')); +} +``` + +Usage - Opt-In Handler (Recipient Clicks Opt In Link) +----------------------------------------------------- +Here's how to use Opt-In Handler to validate an Opt-In Hash. + +```php +# First, instantiate the SDK with your API credentials and domain. +$mg = new Mailgun('key-example'); +$domain = 'example.com'; + +# Next, instantiate an OptInHandler object from the SDK. +$optInHandler = $mg->OptInHandler(); + +# Next, grab the hash. +$inboundHash = $_GET['hash']; +$secretPassphrase = 'a_secret_passphrase'; + +# Now, validate the captured hash. +$hashValidation = $optInHandler->validateHash($secretPassphrase, $inboundHash); + +# Lastly, check to see if we have results, parse, subscribe, and send confirmation. +if($hashValidation){ + $validatedList = $hashValidation['mailingList']; + $validatedRecipient = $hashValidation['recipientAddress']; + + $mg->put("lists/$validatedList/members/$validatedRecipient", + array('address' => $validatedRecipient, + 'subscribed' => 'yes')); + + $mg->sendMessage($domain, array('from' => 'bob@example.com', + 'to' => $validatedRecipient, + 'subject' => 'Confirmation Received!', + 'html' => "Hello,

We've successfully subscribed + you to the list, $validatedList!

Thank you! + ")); +} +``` + +A few notes: +1. 'a_secret_passphrase' can be anything. It's used as the *key* in hashing, +since your email address will vary. +2. validateHash() will return an array containing the recipient address and list +address. +3. You should *always* send an email confirmation before and after the +subscription request. +4. WARNING: On $_GET['hash'], you need to sanitize this value to prevent +malicious attempts to inject code. + +Available Functions +----------------------------------------------------- + +`string generateHash(string $mailingList, string $secretAppId, string $recipientAddress)` + +`array validateHash(string $secretAppId, string $uniqueHash)` + +More Documentation +------------------ +See the official [Mailgun Docs](http://documentation.mailgun.com/api-sending.html) +for more information. diff --git a/sites/all/libraries/mailgun/src/Mailgun/Mailgun.php b/sites/all/libraries/mailgun/src/Mailgun/Mailgun.php new file mode 100644 index 00000000..67778016 --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Mailgun.php @@ -0,0 +1,185 @@ +apiKey = $apiKey; + $this->restClient = new RestClient($apiKey, $apiEndpoint, $httpClient); + } + + /** + * This function allows the sending of a fully formed message OR a custom + * MIME string. If sending MIME, the string must be passed in to the 3rd + * position of the function call. + * + * @param string $workingDomain + * @param array $postData + * @param array $postFiles + * @throws Exceptions\MissingRequiredMIMEParameters + */ + public function sendMessage($workingDomain, $postData, $postFiles = array()){ + if(is_array($postFiles)){ + return $this->post("$workingDomain/messages", $postData, $postFiles); + } + else if(is_string($postFiles)){ + + $tempFile = tempnam(sys_get_temp_dir(), "MG_TMP_MIME"); + $fileHandle = fopen($tempFile, "w"); + fwrite($fileHandle, $postFiles); + + $result = $this->post("$workingDomain/messages.mime", $postData, array("message" => $tempFile)); + fclose($fileHandle); + unlink($tempFile); + return $result; + } + else{ + throw new Exceptions\MissingRequiredMIMEParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } + } + + /** + * This function checks the signature in a POST request to see if it is + * authentic. + * + * Pass an array of parameters. If you pass nothing, $_POST will be + * used instead. + * + * If this function returns FALSE, you must not process the request. + * You should reject the request with status code 403 Forbidden. + * + * @param array|null $postData + * @return bool + */ + public function verifyWebhookSignature($postData = NULL) { + if(is_null($postData)) { + $postData = $_POST; + } + $hmac = hash_hmac('sha256', "{$postData["timestamp"]}{$postData["token"]}", $this->apiKey); + $sig = $postData['signature']; + if(function_exists('hash_equals')) { + // hash_equals is constant time, but will not be introduced until PHP 5.6 + return hash_equals($hmac, $sig); + } + else { + return ($hmac == $sig); + } + } + + /** + * @param string $endpointUrl + * @param array $postData + * @param array $files + * @return \stdClass + */ + public function post($endpointUrl, $postData = array(), $files = array()){ + return $this->restClient->post($endpointUrl, $postData, $files); + } + + /** + * @param string $endpointUrl + * @param array $queryString + * @return \stdClass + */ + public function get($endpointUrl, $queryString = array()){ + return $this->restClient->get($endpointUrl, $queryString); + } + + /** + * @param string $endpointUrl + * @return \stdClass + */ + public function delete($endpointUrl){ + return $this->restClient->delete($endpointUrl); + } + + /** + * @param string $endpointUrl + * @param array $putData + * @return \stdClass + */ + public function put($endpointUrl, $putData){ + return $this->restClient->put($endpointUrl, $putData); + } + + /** + * @param string $apiVersion + * + * @return Mailgun + */ + public function setApiVersion($apiVersion) + { + $this->restClient->setApiVersion($apiVersion); + + return $this; + } + + /** + * @param boolean $sslEnabled + * + * @return Mailgun + */ + public function setSslEnabled($sslEnabled) + { + $this->restClient->setSslEnabled($sslEnabled); + + return $this; + } + + /** + * @return MessageBuilder + */ + public function MessageBuilder(){ + return new MessageBuilder(); + } + + /** + * @return OptInHandler + */ + public function OptInHandler(){ + return new OptInHandler(); + } + + /** + * @param string $workingDomain + * @param bool $autoSend + * @return BatchMessage + */ + public function BatchMessage($workingDomain, $autoSend = true){ + return new BatchMessage($this->restClient, $workingDomain, $autoSend); + } +} diff --git a/sites/all/libraries/mailgun/src/Mailgun/Messages/BatchMessage.php b/sites/all/libraries/mailgun/src/Mailgun/Messages/BatchMessage.php new file mode 100644 index 00000000..003178e0 --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Messages/BatchMessage.php @@ -0,0 +1,141 @@ +batchRecipientAttributes = array(); + $this->autoSend = $autoSend; + $this->restClient = $restClient; + $this->workingDomain = $workingDomain; + $this->endpointUrl = $workingDomain . "/messages"; + } + + /** + * @param string $headerName + * @param string $address + * @param array $variables + * @throws MissingRequiredMIMEParameters + * @throws TooManyParameters + */ + protected function addRecipient($headerName, $address, $variables){ + if(array_key_exists($headerName, $this->counters['recipients'])){ + if($this->counters['recipients'][$headerName] == Api::RECIPIENT_COUNT_LIMIT){ + if($this->autoSend == false){ + throw new TooManyParameters(ExceptionMessages::TOO_MANY_RECIPIENTS); + } + $this->sendMessage(); + } + } + + $compiledAddress = $this->parseAddress($address, $variables); + + if(isset($this->message[$headerName])){ + array_push($this->message[$headerName], $compiledAddress); + } + elseif($headerName == "h:reply-to"){ + $this->message[$headerName] = $compiledAddress; + } + else{ + $this->message[$headerName] = array($compiledAddress); + } + + if(array_key_exists($headerName, $this->counters['recipients'])){ + $this->counters['recipients'][$headerName] += 1; + if(!array_key_exists("id", $variables)){ + $variables['id'] = $this->counters['recipients'][$headerName]; + } + } + $this->batchRecipientAttributes["$address"] = $variables; + } + + /** + * @param array $message + * @param array $files + * @throws MissingRequiredMIMEParameters + */ + public function sendMessage($message = array(), $files = array()){ + if(count($message) < 1){ + $message = $this->message; + $files = $this->files; + } + if(!array_key_exists("from", $message)){ + throw new MissingRequiredMIMEParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } + elseif(!array_key_exists("to", $message)){ + throw new MissingRequiredMIMEParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } + elseif(!array_key_exists("subject", $message)){ + throw new MissingRequiredMIMEParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } + elseif((!array_key_exists("text", $message) && !array_key_exists("html", $message))){ + throw new MissingRequiredMIMEParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } + else{ + $message["recipient-variables"] = json_encode($this->batchRecipientAttributes); + $response = $this->restClient->post($this->endpointUrl, $message, $files); + $this->batchRecipientAttributes = array(); + $this->counters['recipients']['to'] = 0; + $this->counters['recipients']['cc'] = 0; + $this->counters['recipients']['bcc'] = 0; + unset($this->message["to"]); + array_push($this->messageIds, $response->http_response_body->id); + } + } + + /** + * @throws MissingRequiredMIMEParameters + */ + public function finalize(){ + $this->sendMessage(); + } + + /** + * @return string[] + */ + public function getMessageIds(){ + return $this->messageIds; + } +} diff --git a/sites/all/libraries/mailgun/src/Mailgun/Messages/Exceptions/InvalidParameter.php b/sites/all/libraries/mailgun/src/Mailgun/Messages/Exceptions/InvalidParameter.php new file mode 100644 index 00000000..9ea1dfc2 --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Messages/Exceptions/InvalidParameter.php @@ -0,0 +1,4 @@ + array( + 'to' => 0, + 'cc' => 0, + 'bcc' => 0 + ), + 'attributes' => array( + 'attachment' => 0, + 'campaign_id' => 0, + 'custom_option' => 0, + 'tag' => 0 + ) + ); + + /** + * @param array $params + * @param string $key + * @param mixed $default + * @return mixed + */ + protected function safeGet($params, $key, $default) + { + if (array_key_exists($key, $params)) { + return $params[$key]; + } + + return $default; + } + + /** + * @param array $params + * @return mixed|string + */ + protected function getFullName($params) + { + if (array_key_exists("first", $params)) { + $first = $this->safeGet($params, "first", ""); + $last = $this->safeGet($params, "last", ""); + + return trim("$first $last"); + } + + return $this->safeGet($params, "full_name", ""); + } + + /** + * @param string $address + * @param array $variables + * @return string + */ + protected function parseAddress($address, $variables) + { + if (!is_array($variables)) { + return $address; + } + $fullName = $this->getFullName($variables); + if ($fullName != null) { + return "'$fullName' <$address>"; + } + + return $address; + } + + /** + * @param string $headerName + * @param string $address + * @param array $variables + */ + protected function addRecipient($headerName, $address, $variables) + { + $compiledAddress = $this->parseAddress($address, $variables); + + if (isset($this->message[$headerName])) { + array_push($this->message[$headerName], $compiledAddress); + } elseif ($headerName == "h:reply-to") { + $this->message[$headerName] = $compiledAddress; + } else { + $this->message[$headerName] = array($compiledAddress); + } + if (array_key_exists($headerName, $this->counters['recipients'])) { + $this->counters['recipients'][$headerName] += 1; + } + } + + /** + * @param string $address + * @param array|null $variables + * @return mixed + * @throws TooManyParameters + */ + public function addToRecipient($address, $variables = null) + { + if ($this->counters['recipients']['to'] > Api::RECIPIENT_COUNT_LIMIT) { + throw new TooManyParameters(ExceptionMessages::TOO_MANY_PARAMETERS_RECIPIENT); + } + $this->addRecipient("to", $address, $variables); + + return end($this->message['to']); + } + + /** + * @param string $address + * @param array|null $variables + * @return mixed + * @throws TooManyParameters + */ + public function addCcRecipient($address, $variables = null) + { + if ($this->counters['recipients']['cc'] > Api::RECIPIENT_COUNT_LIMIT) { + throw new TooManyParameters(ExceptionMessages::TOO_MANY_PARAMETERS_RECIPIENT); + } + $this->addRecipient("cc", $address, $variables); + + return end($this->message['cc']); + } + + /** + * @param string $address + * @param array|null $variables + * @return mixed + * @throws TooManyParameters + */ + public function addBccRecipient($address, $variables = null) + { + if ($this->counters['recipients']['bcc'] > Api::RECIPIENT_COUNT_LIMIT) { + throw new TooManyParameters(ExceptionMessages::TOO_MANY_PARAMETERS_RECIPIENT); + } + $this->addRecipient("bcc", $address, $variables); + + return end($this->message['bcc']); + } + + /** + * @param string $address + * @param array|null $variables + * @return mixed + */ + public function setFromAddress($address, $variables = null) + { + $this->addRecipient("from", $address, $variables); + + return $this->message['from']; + } + + /** + * @param string $address + * @param array|null $variables + * @return mixed + */ + public function setReplyToAddress($address, $variables = null) + { + $this->addRecipient("h:reply-to", $address, $variables); + + return $this->message['h:reply-to']; + } + + /** + * @param string $subject + * @return mixed + */ + public function setSubject($subject = "") + { + if ($subject == null || $subject == "") { + $subject = " "; + } + $this->message['subject'] = $subject; + + return $this->message['subject']; + } + + /** + * @param string $headerName + * @param mixed $headerData + * @return mixed + */ + public function addCustomHeader($headerName, $headerData) + { + if (!preg_match("/^h:/i", $headerName)) { + $headerName = "h:" . $headerName; + } + $this->message[$headerName] = array($headerData); + + return $this->message[$headerName]; + } + + /** + * @param string $textBody + * @return string + */ + public function setTextBody($textBody) + { + if ($textBody == null || $textBody == "") { + $textBody = " "; + } + $this->message['text'] = $textBody; + + return $this->message['text']; + } + + /** + * @param string $htmlBody + * @return string + */ + public function setHtmlBody($htmlBody) + { + if ($htmlBody == null || $htmlBody == "") { + $htmlBody = " "; + } + $this->message['html'] = $htmlBody; + + return $this->message['html']; + } + + /** + * @param string $attachmentPath + * @param string|null $attachmentName + * @return bool + */ + public function addAttachment($attachmentPath, $attachmentName = null) + { + if (isset($this->files["attachment"])) { + $attachment = array( + 'filePath' => $attachmentPath, + 'remoteName' => $attachmentName + ); + array_push($this->files["attachment"], $attachment); + } else { + $this->files["attachment"] = array( + array( + 'filePath' => $attachmentPath, + 'remoteName' => $attachmentName + ) + ); + } + + return true; + } + + /** + * @param string $inlineImagePath + * @param string|null $inlineImageName + * @throws InvalidParameter + */ + public function addInlineImage($inlineImagePath, $inlineImageName = null) + { + if (preg_match("/^@/", $inlineImagePath)) { + if (isset($this->files['inline'])) { + $inlineAttachment = array( + 'filePath' => $inlineImagePath, + 'remoteName' => $inlineImageName + ); + array_push($this->files['inline'], $inlineAttachment); + } else { + $this->files['inline'] = array( + array( + 'filePath' => $inlineImagePath, + 'remoteName' => $inlineImageName + ) + ); + } + + return true; + } else { + throw new InvalidParameter(ExceptionMessages::INVALID_PARAMETER_INLINE); + } + } + + /** + * @param boolean $testMode + * @return string + */ + public function setTestMode($testMode) + { + if (filter_var($testMode, FILTER_VALIDATE_BOOLEAN)) { + $testMode = "yes"; + } else { + $testMode = "no"; + } + $this->message['o:testmode'] = $testMode; + + return $this->message['o:testmode']; + } + + /** + * @param string|int $campaignId + * @return string|int + * @throws TooManyParameters + */ + public function addCampaignId($campaignId) + { + if ($this->counters['attributes']['campaign_id'] < Api::CAMPAIGN_ID_LIMIT) { + if (isset($this->message['o:campaign'])) { + array_push($this->message['o:campaign'], $campaignId); + } else { + $this->message['o:campaign'] = array($campaignId); + } + $this->counters['attributes']['campaign_id'] += 1; + + return $this->message['o:campaign']; + } else { + throw new TooManyParameters(ExceptionMessages::TOO_MANY_PARAMETERS_CAMPAIGNS); + } + } + + /** + * @param string $tag + * @throws TooManyParameters + */ + public function addTag($tag) + { + if ($this->counters['attributes']['tag'] < Api::TAG_LIMIT) { + if (isset($this->message['o:tag'])) { + array_push($this->message['o:tag'], $tag); + } else { + $this->message['o:tag'] = array($tag); + } + $this->counters['attributes']['tag'] += 1; + + return $this->message['o:tag']; + } else { + throw new TooManyParameters(ExceptionMessages::TOO_MANY_PARAMETERS_TAGS); + } + } + + /** + * @param boolean $enabled + * @return mixed + */ + public function setDkim($enabled) + { + if (filter_var($enabled, FILTER_VALIDATE_BOOLEAN)) { + $enabled = "yes"; + } else { + $enabled = "no"; + } + $this->message["o:dkim"] = $enabled; + + return $this->message["o:dkim"]; + } + + /** + * @param boolean $enabled + * @return string + */ + public function setOpenTracking($enabled) + { + if (filter_var($enabled, FILTER_VALIDATE_BOOLEAN)) { + $enabled = "yes"; + } else { + $enabled = "no"; + } + $this->message['o:tracking-opens'] = $enabled; + + return $this->message['o:tracking-opens']; + } + + /** + * @param boolean $enabled + * @return string + */ + public function setClickTracking($enabled) + { + if (filter_var($enabled, FILTER_VALIDATE_BOOLEAN)) { + $enabled = "yes"; + } elseif ($enabled == "html") { + $enabled = "html"; + } else { + $enabled = "no"; + } + $this->message['o:tracking-clicks'] = $enabled; + + return $this->message['o:tracking-clicks']; + } + + /** + * @param string $timeDate + * @param string|null $timeZone + * @return string + */ + public function setDeliveryTime($timeDate, $timeZone = null) + { + if (isset($timeZone)) { + $timeZoneObj = new \DateTimeZone("$timeZone"); + } else { + $timeZoneObj = new \DateTimeZone(Api::DEFAULT_TIME_ZONE); + } + + $dateTimeObj = new \DateTime($timeDate, $timeZoneObj); + $formattedTimeDate = $dateTimeObj->format(\DateTime::RFC2822); + $this->message['o:deliverytime'] = $formattedTimeDate; + + return $this->message['o:deliverytime']; + } + + /** + * @param string $customName + * @param mixed $data + */ + public function addCustomData($customName, $data) + { + $this->message['v:' . $customName] = json_encode($data); + } + + /** + * @param string $parameterName + * @param mixed $data + * @return mixed + */ + public function addCustomParameter($parameterName, $data) + { + if (isset($this->message[$parameterName])) { + array_push($this->message[$parameterName], $data); + + return $this->message[$parameterName]; + } else { + $this->message[$parameterName] = array($data); + + return $this->message[$parameterName]; + } + } + + /** + * @param array $message + */ + public function setMessage($message) + { + $this->message = $message; + } + + /** + * @return array + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return array + */ + public function getFiles() + { + return $this->files; + } +} diff --git a/sites/all/libraries/mailgun/src/Mailgun/Messages/README.md b/sites/all/libraries/mailgun/src/Mailgun/Messages/README.md new file mode 100644 index 00000000..88a48cba --- /dev/null +++ b/sites/all/libraries/mailgun/src/Mailgun/Messages/README.md @@ -0,0 +1,138 @@ +Mailgun - Messages +==================== + +This is the Mailgun PHP *Message* utilities. + +The below assumes you've already installed the Mailgun PHP SDK in to your +project. If not, go back to the master README for instructions. + +There are two utilities included, Message Builder and Batch Message. + +Message Builder: Allows you to build a message object by calling methods for +each MIME attribute. +Batch Message: Inherits Message Builder and allows you to iterate through +recipients from a list. Messages will fire after the 1,000th recipient has been +added. + +Usage - Message Builder +----------------------- +Here's how to use Message Builder to build your Message. + +```php +# First, instantiate the SDK with your API credentials and define your domain. +$mg = new Mailgun("key-example"); +$domain = "example.com"; + +# Next, instantiate a Message Builder object from the SDK. +$messageBldr = $mg->MessageBuilder(); + +# Define the from address. +$messageBldr->setFromAddress("me@example.com", array("first"=>"PHP", "last" => "SDK")); +# Define a to recipient. +$messageBldr->addToRecipient("john.doe@example.com", array("first" => "John", "last" => "Doe")); +# Define a cc recipient. +$messageBldr->addCcRecipient("sally.doe@example.com", array("first" => "Sally", "last" => "Doe")); +# Define the subject. +$messageBldr->setSubject("A message from the PHP SDK using Message Builder!"); +# Define the body of the message. +$messageBldr->setTextBody("This is the text body of the message!"); + +# Other Optional Parameters. +$messageBldr->addCampaignId("My-Awesome-Campaign"); +$messageBldr->addCustomHeader("Customer-Id", "12345"); +$messageBldr->addAttachment("@/tron.jpg"); +$messageBldr->setDeliveryTime("tomorrow 8:00AM", "PST"); +$messageBldr->setClickTracking(true); + +# Finally, send the message. +$mg->post("{$domain}/messages", $messageBldr->getMessage(), $messageBldr->getFiles()); +``` + +Available Functions +----------------------------------------------------- + +`string addToRecipient(string $address, array $attributes)` + +`string addCcRecipient(string $address, array $attributes)` + +`string addBccRecipient(string $address, array $attributes)` + +`string setFromAddress(string $address, array $attributes)` + +`string setSubject(string $subject)` + +`string setTextBody(string $textBody)` + +`string setHtmlBody(string $htmlBody)` + +`bool addAttachment(string $attachmentPath)` + +`bool addInlineImage(string $inlineImagePath)` + +`string setTestMode(bool $testMode)` + +`string addCampaignId(string $campaignId)` + +`string setDkim(bool $enabled)` + +`string setOpenTracking($enabled)` + +`string setClickTracking($enabled)` + +`string setDeliveryTime(string $timeDate, string $timeZone)` + +`string addCustomData(string $optionName, string $data)` + +`string addCustomParameter(string $parameterName, string $data)` + +`array getMessage()` + +`array getFiles()` + + +Usage - Batch Message +--------------------- +Here's how to use Batch Message to easily handle batch sending jobs. + +```php +# First, instantiate the SDK with your API credentials and define your domain. +$mg = new Mailgun("key-example"); +$domain = "example.com"; + +# Next, instantiate a Message Builder object from the SDK, pass in your sending +domain. +$batchMsg = $mg->BatchMessage($domain); + +# Define the from address. +$batchMsg->setFromAddress("me@example.com", array("first"=>"PHP", "last" => "SDK")); +# Define the subject. +$batchMsg->setSubject("A Batch Message from the PHP SDK!"); +# Define the body of the message. +$batchMsg->setTextBody("This is the text body of the message!"); + +# Next, let's add a few recipients to the batch job. +$batchMsg->addToRecipient("john.doe@example.com", array("first" => "John", "last" => "Doe")); +$batchMsg->addToRecipient("sally.doe@example.com", array("first" => "Sally", "last" => "Doe")); +$batchMsg->addToRecipient("mike.jones@example.com", array("first" => "Mike", "last" => "Jones")); +... +// After 1,000 recipeints, Batch Message will automatically post your message to +the messages endpoint. + +// Call finalize() to send any remaining recipients still in the buffer. +$batchMsg->finalize(); + +``` + +Available Functions (Inherits all Batch Message and Messages Functions) +----------------------------------------------------------------------- + +`addToRecipient(string $address, string $attributes)` + +`sendMessage(array $message, array $files)` + +`array finalize()` + +More Documentation +------------------ +See the official [Mailgun Docs](http://documentation.mailgun.com/api-sending.html) +for more information. diff --git a/sites/all/libraries/mailgun/tests/Bootstrap.php b/sites/all/libraries/mailgun/tests/Bootstrap.php new file mode 100644 index 00000000..da835f11 --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Bootstrap.php @@ -0,0 +1,5 @@ +client = new Mailgun("My-Super-Awesome-API-Key"); + $this->optInHandler = $this->client->OptInHandler(); + } + + public function testReturnOfGenerateHash() + { + $generatedHash = $this->optInHandler->generateHash( + 'mytestlist@example.com', + 'mysupersecretappid', + 'testrecipient@example.com' + ); + $knownHash = "eyJoIjoiMTllODc2YWNkMWRmNzk4NTc0ZTU0YzhjMzIzOTNiYTNjNzdhNGMxOCIsInAiOiJleUp5SWpvaWRHVnpkSEpsWTJsd2FXVnVkRUJsZUdGdGNHeGxMbU52YlNJc0ltd2lPaUp0ZVhSbGMzUnNhWE4wUUdWNFlXMXdiR1V1WTI5dEluMD0ifQ%3D%3D"; + $this->assertEquals($generatedHash, $knownHash); + } + + public function testGoodHash() + { + $validation = $this->optInHandler->validateHash( + 'mysupersecretappid', + 'eyJoIjoiMTllODc2YWNkMWRmNzk4NTc0ZTU0YzhjMzIzOTNiYTNjNzdhNGMxOCIsInAiOiJleUp5SWpvaWRHVnpkSEpsWTJsd2FXVnVkRUJsZUdGdGNHeGxMbU52YlNJc0ltd2lPaUp0ZVhSbGMzUnNhWE4wUUdWNFlXMXdiR1V1WTI5dEluMD0ifQ%3D%3D' + ); + $this->assertArrayHasKey('recipientAddress', $validation); + $this->assertArrayHasKey('mailingList', $validation); + } + + public function testBadHash() + { + $validation = $this->optInHandler->validateHash( + 'mybadsecretappid', + 'eyJoIjoiMTllODc2YWNkMWRmNzk4NTc0ZTU0YzhjMzIzOTNiYTNjNzdhNGMxOCIsInAiOiJleUp5SWpvaWRHVnpkSEpsWTJsd2FXVnVkRUJsZUdGdGNHeGxMbU52YlNJc0ltd2lPaUp0ZVhSbGMzUnNhWE4wUUdWNFlXMXdiR1V1WTI5dEluMD0ifQ%3D%3D' + ); + $this->assertFalse($validation); + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTest.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTest.php new file mode 100644 index 00000000..c73c1aab --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTest.php @@ -0,0 +1,36 @@ +setExpectedException("\\Mailgun\\Messages\\Exceptions\\MissingRequiredMIMEParameters"); + + $client = new Mailgun(); + $client->sendMessage("test.mailgun.com", "etss", 1); + } + + public function testVerifyWebhookGood() { + $client = new Mailgun('key-3ax6xnjp29jd6fds4gc373sgvjxteol0'); + $postData = array( + 'timestamp' => '1403645220', + 'token' => '5egbgr1vjgqxtrnp65xfznchgdccwh5d6i09vijqi3whgowmn6', + 'signature' => '9cfc5c41582e51246e73c88d34db3af0a3a2692a76fbab81492842f000256d33', + ); + assert($client->verifyWebhookSignature($postData)); + } + + public function testVerifyWebhookBad() { + $client = new Mailgun('key-3ax6xnjp29jd6fds4gc373sgvjxteol0'); + $postData = array( + 'timestamp' => '1403645220', + 'token' => 'owyldpe6nxhmrn78epljl6bj0orrki1u3d2v5e6cnlmmuox8jr', + 'signature' => '9cfc5c41582e51246e73c88d34db3af0a3a2692a76fbab81492842f000256d33', + ); + assert(!$client->verifyWebhookSignature($postData)); + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTestCase.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTestCase.php new file mode 100644 index 00000000..3d8f2e65 --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/MailgunTestCase.php @@ -0,0 +1,7 @@ +client = new Mailgun("My-Super-Awesome-API-Key"); + } + + public function testBlankInstantiation() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $this->assertTrue(is_array($message->getMessage())); + } + + public function testAddRecipient() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addToRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("to" => array("'Test User' ")), $messageObj); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('counters'); + $property->setAccessible(true); + $array = $property->getValue($message); + $this->assertEquals(1, $array['recipients']['to']); + } + + public function testRecipientVariablesOnTo() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addToRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("to" => array("'Test User' ")), $messageObj); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('batchRecipientAttributes'); + $property->setAccessible(true); + $propertyValue = $property->getValue($message); + $this->assertEquals("Test", $propertyValue['test@samples.mailgun.org']['first']); + $this->assertEquals("User", $propertyValue['test@samples.mailgun.org']['last']); + } + + public function testRecipientVariablesOnCc() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addCcRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("cc" => array("'Test User' ")), $messageObj); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('batchRecipientAttributes'); + $property->setAccessible(true); + $propertyValue = $property->getValue($message); + $this->assertEquals("Test", $propertyValue['test@samples.mailgun.org']['first']); + $this->assertEquals("User", $propertyValue['test@samples.mailgun.org']['last']); + } + + public function testRecipientVariablesOnBcc() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addBccRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("bcc" => array("'Test User' ")), $messageObj); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('batchRecipientAttributes'); + $property->setAccessible(true); + $propertyValue = $property->getValue($message); + $this->assertEquals("Test", $propertyValue['test@samples.mailgun.org']['first']); + $this->assertEquals("User", $propertyValue['test@samples.mailgun.org']['last']); + } + + public function testAddMultipleBatchRecipients() + { + $message = $this->client->BatchMessage($this->sampleDomain); + for ($i = 0; $i < 100; $i++) { + $message->addToRecipient("$i@samples.mailgun.org", array("first" => "Test", "last" => "User $i")); + } + $messageObj = $message->getMessage(); + $this->assertEquals(100, count($messageObj["to"])); + } + + public function testMaximumBatchSize() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->setFromAddress("samples@mailgun.org", array("first" => "Test", "last" => "User")); + $message->setSubject("This is the subject of the message!"); + $message->setTextBody("This is the text body of the message!"); + for ($i = 0; $i < 1001; $i++) { + $message->addToRecipient("$i@samples.mailgun.org", array("first" => "Test", "last" => "User $i")); + } + $messageObj = $message->getMessage(); + $this->assertEquals(1, count($messageObj["to"])); + } + + public function testAttributeResetOnEndBatchMessage() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addToRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $message->setFromAddress("samples@mailgun.org", array("first" => "Test", "last" => "User")); + $message->setSubject("This is the subject of the message!"); + $message->setTextBody("This is the text body of the message!"); + $message->finalize(); + $messageObj = $message->getMessage(); + $this->assertTrue(true, empty($messageObj)); + } + + public function testDefaultIDInVariables() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addToRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('batchRecipientAttributes'); + $property->setAccessible(true); + $propertyValue = $property->getValue($message); + $this->assertEquals(1, $propertyValue['test-user@samples.mailgun.org']['id']); + } + + public function testgetMessageIds() + { + $message = $this->client->BatchMessage($this->sampleDomain); + $message->addToRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $message->setFromAddress("samples@mailgun.org", array("first" => "Test", "last" => "User")); + $message->setSubject("This is the subject of the message!"); + $message->setTextBody("This is the text body of the message!"); + $message->finalize(); + + $this->assertEquals(array("1234"), $message->getMessageIds()); + } + + public function testInvalidMissingRequiredMIMEParametersExceptionGetsFlungNoFrom() + { + $this->setExpectedException("\\Mailgun\\Messages\\Exceptions\\MissingRequiredMIMEParameters"); + + $message = $this->client->BatchMessage($this->sampleDomain); + $message->sendMessage(array(1, 2, 3)); + } + + public function testInvalidMissingRequiredMIMEParametersExceptionGetsFlungNoTo() + { + $this->setExpectedException("\\Mailgun\\Messages\\Exceptions\\MissingRequiredMIMEParameters"); + + $message = $this->client->BatchMessage($this->sampleDomain); + $message->sendMessage(array("from" => 1, 2, 3)); + } + + public function testInvalidMissingRequiredMIMEParametersExceptionGetsFlungNoSubject() + { + $this->setExpectedException("\\Mailgun\\Messages\\Exceptions\\MissingRequiredMIMEParameters"); + + $message = $this->client->BatchMessage($this->sampleDomain); + $message->sendMessage(array("from" => 1, "to" => 2, 3)); + } + + public function testInvalidMissingRequiredMIMEParametersExceptionGetsFlungNoTextOrHtml() + { + $this->setExpectedException("\\Mailgun\\Messages\\Exceptions\\MissingRequiredMIMEParameters"); + + $message = $this->client->BatchMessage($this->sampleDomain); + $message->sendMessage(array("from" => 1, "to" => 2, "subject" => 3)); + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/MessageBuilderTest.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/MessageBuilderTest.php new file mode 100644 index 00000000..8daf2e33 --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/MessageBuilderTest.php @@ -0,0 +1,356 @@ +client = new Mailgun(); + } + + public function testBlankInstantiation() + { + $message = $this->client->MessageBuilder(); + $this->assertTrue(is_array($message->getMessage())); + } + + public function testCountersSetToZero() + { + $message = $this->client->MessageBuilder(); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('counters'); + $property->setAccessible(true); + $propertyValue = $property->getValue($message); + $this->assertEquals(0, $propertyValue['recipients']['to']); + $this->assertEquals(0, $propertyValue['recipients']['cc']); + $this->assertEquals(0, $propertyValue['recipients']['bcc']); + $this->assertEquals(0, $propertyValue['attributes']['attachment']); + $this->assertEquals(0, $propertyValue['attributes']['campaign_id']); + $this->assertEquals(0, $propertyValue['attributes']['custom_option']); + $this->assertEquals(0, $propertyValue['attributes']['tag']); + } + + public function testAddToRecipient() + { + $message = $this->client->MessageBuilder(); + $message->addToRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("to" => array("'Test User' ")), $messageObj); + } + + public function testAddCcRecipient() + { + $message = $this->client->MessageBuilder(); + $message->addCcRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("cc" => array("'Test User' ")), $messageObj); + } + + public function testAddBccRecipient() + { + $message = $this->client->MessageBuilder(); + $message->addBccRecipient("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("bcc" => array("'Test User' ")), $messageObj); + } + + public function testToRecipientCount() + { + $message = $this->client->MessageBuilder(); + $message->addToRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('counters'); + $property->setAccessible(true); + $array = $property->getValue($message); + $this->assertEquals(1, $array['recipients']['to']); + } + + public function testCcRecipientCount() + { + $message = $this->client->MessageBuilder(); + $message->addCcRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('counters'); + $property->setAccessible(true); + $array = $property->getValue($message); + $this->assertEquals(1, $array['recipients']['cc']); + } + + public function testBccRecipientCount() + { + $message = $this->client->MessageBuilder(); + $message->addBccRecipient("test-user@samples.mailgun.org", array("first" => "Test", "last" => "User")); + + $reflectionClass = new \ReflectionClass(get_class($message)); + $property = $reflectionClass->getProperty('counters'); + $property->setAccessible(true); + $array = $property->getValue($message); + $this->assertEquals(1, $array['recipients']['bcc']); + } + + public function testSetFromAddress() + { + $message = $this->client->MessageBuilder(); + $message->setFromAddress("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("from" => array("'Test User' ")), $messageObj); + } + + public function testSetReplyTo() + { + $message = $this->client->MessageBuilder(); + $message->setReplyToAddress("test@samples.mailgun.org", array("first" => "Test", "last" => "User")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("h:reply-to" => "'Test User' "), $messageObj); + } + + public function testSetSubject() + { + $message = $this->client->MessageBuilder(); + $message->setSubject("Test Subject"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("subject" => "Test Subject"), $messageObj); + } + + public function testAddCustomHeader() + { + $message = $this->client->MessageBuilder(); + $message->addCustomHeader("My-Header", "123"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("h:My-Header" => array("123")), $messageObj); + } + + public function testSetTextBody() + { + $message = $this->client->MessageBuilder(); + $message->setTextBody("This is the text body!"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("text" => "This is the text body!"), $messageObj); + } + + public function testSetHtmlBody() + { + $message = $this->client->MessageBuilder(); + $message->setHtmlBody("This is an awesome email"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("html" => "This is an awesome email"), $messageObj); + } + + public function testAddAttachments() + { + $message = $this->client->MessageBuilder(); + $message->addAttachment("@../TestAssets/mailgun_icon.png"); + $message->addAttachment("@../TestAssets/rackspace_logo.png"); + $messageObj = $message->getFiles(); + $this->assertEquals( + array( + array( + 'filePath' => "@../TestAssets/mailgun_icon.png", + 'remoteName' => null + ), + array( + 'filePath' => "@../TestAssets/rackspace_logo.png", + 'remoteName' => null + ) + ), + $messageObj["attachment"] + ); + } + + public function testAddInlineImages() + { + $message = $this->client->MessageBuilder(); + $message->addInlineImage("@../TestAssets/mailgun_icon.png"); + $message->addInlineImage("@../TestAssets/rackspace_logo.png"); + $messageObj = $message->getFiles(); + $this->assertEquals( + array( + array( + 'filePath' => "@../TestAssets/mailgun_icon.png", + 'remoteName' => null + ), + array( + 'filePath' => "@../TestAssets/rackspace_logo.png", + 'remoteName' => null + ) + ), + $messageObj['inline'] + ); + } + + public function testAddAttachmentsPostName() + { + $message = $this->client->MessageBuilder(); + $message->addAttachment('@../TestAssets/mailgun_icon.png', 'mg_icon.png'); + $message->addAttachment('@../TestAssets/rackspace_logo.png', 'rs_logo.png'); + $messageObj = $message->getFiles(); + $this->assertEquals( + array( + array( + 'filePath' => '@../TestAssets/mailgun_icon.png', + 'remoteName' => 'mg_icon.png' + ), + array( + 'filePath' => '@../TestAssets/rackspace_logo.png', + 'remoteName' => 'rs_logo.png' + ) + ), + $messageObj["attachment"] + ); + } + + public function testAddInlineImagePostName() + { + $message = $this->client->MessageBuilder(); + $message->addInlineImage('@../TestAssets/mailgun_icon.png', 'mg_icon.png'); + $message->addInlineImage('@../TestAssets/rackspace_logo.png', 'rs_logo.png'); + $messageObj = $message->getFiles(); + $this->assertEquals( + array( + array( + 'filePath' => '@../TestAssets/mailgun_icon.png', + 'remoteName' => 'mg_icon.png' + ), + array( + 'filePath' => '@../TestAssets/rackspace_logo.png', + 'remoteName' => 'rs_logo.png' + ) + ), + $messageObj['inline'] + ); + } + + public function testsetTestMode() + { + $message = $this->client->MessageBuilder(); + $message->setTestMode(true); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:testmode" => "yes"), $messageObj); + $message->setTestMode(false); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:testmode" => "no"), $messageObj); + $message->setTestMode("yes"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:testmode" => "yes"), $messageObj); + $message->setTestMode("no"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:testmode" => "no"), $messageObj); + } + + public function addCampaignId() + { + $message = $this->client->MessageBuilder(); + $message->addCampaignId("ABC123"); + $message->addCampaignId("XYZ987"); + $message->addCampaignId("TUV456"); + $message->addCampaignId("NONO123"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:campaign" => array("ABC123", "XYZ987", "TUV456")), $messageObj); + } + + public function testSetDkim() + { + $message = $this->client->MessageBuilder(); + $message->setDkim(true); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:dkim" => "yes"), $messageObj); + $message->setDkim(false); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:dkim" => "no"), $messageObj); + $message->setDkim("yes"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:dkim" => "yes"), $messageObj); + $message->setDkim("no"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:dkim" => "no"), $messageObj); + } + + public function testSetClickTracking() + { + $message = $this->client->MessageBuilder(); + $message->setClickTracking(true); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-clicks" => "yes"), $messageObj); + $message->setClickTracking(false); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-clicks" => "no"), $messageObj); + $message->setClickTracking("yes"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-clicks" => "yes"), $messageObj); + $message->setClickTracking("no"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-clicks" => "no"), $messageObj); + } + + public function testSetOpenTracking() + { + $message = $this->client->MessageBuilder(); + $message->setOpenTracking(true); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-opens" => "yes"), $messageObj); + $message->setOpenTracking(false); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-opens" => "no"), $messageObj); + $message->setOpenTracking("yes"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-opens" => "yes"), $messageObj); + $message->setOpenTracking("no"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:tracking-opens" => "no"), $messageObj); + } + + public function testSetDeliveryTime() + { + $message = $this->client->MessageBuilder(); + $message->setDeliveryTime("January 15, 2014 8:00AM", "CST"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:deliverytime" => "Wed, 15 Jan 2014 08:00:00 -0600"), $messageObj); + $message->setDeliveryTime("January 15, 2014 8:00AM", "UTC"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:deliverytime" => "Wed, 15 Jan 2014 08:00:00 +0000"), $messageObj); + $message->setDeliveryTime("January 15, 2014 8:00AM"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:deliverytime" => "Wed, 15 Jan 2014 08:00:00 +0000"), $messageObj); + $message->setDeliveryTime("1/15/2014 13:50:01", "CDT"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("o:deliverytime" => "Wed, 15 Jan 2014 13:50:01 -0600"), $messageObj); + // https://github.com/mailgun/mailgun-php/pull/42 + // https://github.com/mailgun/mailgun-php/issues/43 + //$message->setDeliveryTime("first saturday of July 2013 8:00AM", "CDT"); + //$messageObj = $message->getMessage(); + //$this->assertEquals(array("o:deliverytime" => "Sat, 06 Jul 2013 08:00:00 -0500"), $messageObj); + } + + public function testAddCustomData() + { + $message = $this->client->MessageBuilder(); + $message->addCustomData("My-Super-Awesome-Data", array("What" => "Mailgun Rocks!")); + $messageObj = $message->getMessage(); + $this->assertEquals(array("v:My-Super-Awesome-Data" => "{\"What\":\"Mailgun Rocks!\"}"), $messageObj); + } + + public function testAddCustomParameter() + { + $message = $this->client->MessageBuilder(); + $message->addCustomParameter("my-option", "yes"); + $message->addCustomParameter("o:my-other-option", "no"); + $messageObj = $message->getMessage(); + $this->assertEquals(array("my-option" => array("yes"), "o:my-other-option" => array("no")), $messageObj); + } + + public function testSetMessage() + { + $message = array(1, 2, 3, 4, 5); + $messageBuilder = $this->client->MessageBuilder(); + $messageBuilder->setMessage($message); + + $this->assertEquals($message, $messageBuilder->getMessage()); + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/StandardMessageTest.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/StandardMessageTest.php new file mode 100644 index 00000000..b1f885d1 --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Messages/StandardMessageTest.php @@ -0,0 +1,48 @@ +client = new Mailgun("My-Super-Awesome-API-Key"); + } + + public function testSendMIMEMessage() + { + $customMime = "Received: by luna.mailgun.net with SMTP mgrt 8728174999085; Mon, 10 Jun 2013 09:50:58 +0000 + Mime-Version: 1.0 + Content-Type: text/plain; charset=\"ascii\" + Subject: This is the Subject! + From: Mailgun Testing + To: test@test.mailgun.com + Message-Id: <20130610095049.30790.4334@test.mailgun.com> + Content-Transfer-Encoding: 7bit + X-Mailgun-Sid: WyIxYTdhMyIsICJmaXplcmtoYW5AcXVhZG1zLmluIiwgImExOWQiXQ== + Date: Mon, 10 Jun 2013 09:50:58 +0000 + Sender: test@test.mailgun.com + + Mailgun is testing!"; + $envelopeFields = array('to' => 'test@test.mailgun.org'); + $result = $this->client->sendMessage("test.mailgun.org", $envelopeFields, $customMime); + $this->assertEquals("test.mailgun.org/messages.mime", $result->http_endpoint_url); + } + + public function testSendMessage() + { + $message = array('to' => 'test@test.mailgun.org', + 'from' => 'sender@test.mailgun.org', + 'subject' => 'This is my test subject', + 'text' => 'Testing!' + ); + $result = $this->client->sendMessage("test.mailgun.org", $message); + $this->assertEquals("test.mailgun.org/messages", $result->http_endpoint_url); + + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Connection/TestBroker.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Connection/TestBroker.php new file mode 100644 index 00000000..6652aa59 --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Connection/TestBroker.php @@ -0,0 +1,71 @@ +apiKey = $apiKey; + $this->apiEndpoint = $apiHost; + } + + public function post($endpointUrl, $postData = array(), $files = array()) + { + return $this->testResponseHandler($endpointUrl, $httpResponseCode = 200); + } + + public function get($endpointUrl, $queryString = array()) + { + return $this->testResponseHandler($endpointUrl, $httpResponseCode = 200); + } + + public function delete($endpointUrl) + { + return $this->testResponseHandler($endpointUrl, $httpResponseCode = 200); + } + + public function put($endpointUrl, $queryString) + { + return $this->testResponseHandler($endpointUrl, $httpResponseCode = 200); + } + + public function testResponseHandler($endpointUrl, $httpResponseCode = 200) + { + if ($httpResponseCode === 200) { + $result = new \stdClass(); + $result->http_response_body = new \stdClass(); + $jsonResponseData = json_decode('{"message": "Some JSON Response Data", "id": "1234"}'); + foreach ($jsonResponseData as $key => $value) { + $result->http_response_body->$key = $value; + } + } elseif ($httpResponseCode == 400) { + throw new MissingRequiredMIMEParameters(EXCEPTION_MISSING_REQUIRED_MIME_PARAMETERS); + } elseif ($httpResponseCode == 401) { + throw new InvalidCredentials(EXCEPTION_INVALID_CREDENTIALS); + } elseif ($httpResponseCode == 401) { + throw new GenericHTTPError(EXCEPTION_INVALID_CREDENTIALS); + } elseif ($httpResponseCode == 404) { + throw new MissingEndpoint(EXCEPTION_MISSING_ENDPOINT); + } else { + throw new GenericHTTPError(EXCEPTION_GENERIC_HTTP_ERROR); + + return false; + } + $result->http_response_code = $httpResponseCode; + $result->http_endpoint_url = $endpointUrl; + + return $result; + } + + +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Mailgun.php b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Mailgun.php new file mode 100644 index 00000000..ad57bcce --- /dev/null +++ b/sites/all/libraries/mailgun/tests/Mailgun/Tests/Mock/Mailgun.php @@ -0,0 +1,17 @@ +restClient = new TestBroker($apiKey, $apiEndpoint, $apiVersion); + } +} diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon1.png b/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon1.png new file mode 100644 index 00000000..604f6730 Binary files /dev/null and b/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon1.png differ diff --git a/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon2.png b/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon2.png new file mode 100644 index 00000000..604f6730 Binary files /dev/null and b/sites/all/libraries/mailgun/tests/Mailgun/Tests/TestAssets/mailgun_icon2.png differ diff --git a/sites/all/modules/contrib/mail/mailgun/LICENSE.txt b/sites/all/modules/contrib/mail/mailgun/LICENSE.txt new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/mail/mailgun/mailgun.admin.inc b/sites/all/modules/contrib/mail/mailgun/mailgun.admin.inc new file mode 100644 index 00000000..b5f99f1f --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/mailgun.admin.inc @@ -0,0 +1,221 @@ +documentation for more information.', array('@url' => url('https://www.drupal.org/node/2547591'))), 'error'); + } + + $key = variable_get('mailgun_api_key', ''); + + $form['mailgun_api_key'] = array( + '#title' => t('Mailgun API key'), + '#type' => 'textfield', + '#description' => t('Get your Secret API key from the Mailgun dashboard.', array('@url' => url('https://mailgun.com/app/dashboard'))), + '#default_value' => $key, + '#required' => TRUE, + ); + + $client = FALSE; + if (!empty($key)) { + try { + $client = mailgun_get_client($key); + } catch (Exception $e) { + watchdog('mailgun', 'An exception occurred. @code: @message', array('@code' => $e->getCode(), '@message' => $e->getMessage()), WATCHDOG_WARNING, 'admin/config/system/mailgun'); + drupal_set_message(t('Mailgun: %message', array('%message' => $e->getMessage())), 'error'); + } + } + + // Display settings only when a valid API key is present and client is active + if ($client) { + $domain_options = array( + '_sender' => t('Get domain from sender address'), + ); + $domains = array(); + $result = $client->get('domains'); + if ($result && $result->http_response_code == 200) { + foreach ($result->http_response_body->items as $domain) { + $domains[$domain->name] = $domain; + $domain_options[$domain->name] = $domain->name; + } + } + + $form['mailgun_domain'] = array( + '#title' => t('Domain'), + '#type' => 'select', + '#options' => $domain_options, + '#description' => t('Mails will be sent using this domain'), + '#default_value' => variable_get('mailgun_domain', '_sender'), + ); + + $form['mailgun_test'] = array( + '#title' => t('Test mode'), + '#type' => 'checkbox', + '#default_value' => variable_get('mailgun_test', FALSE), + '#description' => t('Enables sending in test mode'), + ); + + $form['mailgun_queue'] = array( + '#title' => t('Queue mails'), + '#type' => 'checkbox', + '#description' => t('Mails will be queued and sent during cron runs. Useful for sending a large number of emails.'), + '#default_value' => variable_get('mailgun_queue', FALSE), + ); + + $form['mailgun_log'] = array( + '#title' => t('Log mails'), + '#type' => 'checkbox', + '#description' => t('Log mails sent through Mailgun. Should not be enabled on production sites. Messages fail to send will be logged regardless of this setting.'), + '#default_value' => variable_get('mailgun_log', FALSE), + ); + + $formats = array('_none' => t('- None -')); + foreach (filter_formats() as $format) { + if ($format->format == 'php_code') { + continue; + } + $formats[$format->format] = t($format->name); + } + $form['mailgun_format'] = array( + '#title' => t('Text format'), + '#type' => 'select', + '#description' => t('Specify an additional text format to filter the message through before sending the email.'), + '#options' => $formats, + '#default_value' => variable_get('mailgun_format', '_none'), + ); + + $form['defaults'] = array( + '#type' => 'fieldset', + '#title' => t('Default settings'), + '#description' => t('These default settings apply to messages sent using Mailgun and may be overriden on a per-message basis.'), + '#collapsible' => FALSE, + '#collapsed' => FALSE, + ); + + $form['defaults']['mailgun_tracking'] = array( + '#title' => t('Enable tracking'), + '#type' => 'select', + '#options' => array('default' => t('Use default setting'), 'enabled' => t('Enabled'), 'disabled' => t('Disabled')), + '#description' => t('Whether to enable event tracking by default or not. See Tracking Messages for details.', array('@url' => url('https://documentation.mailgun.com/user_manual.html#tracking-messages'))), + '#default_value' => variable_get('mailgun_tracking', 'default'), + ); + + $form['defaults']['mailgun_tracking_clicks'] = array( + '#title' => t('Enable click tracking'), + '#type' => 'select', + '#options' => array('default' => t('Use default setting'), 'enabled' => t('Enabled'), 'disabled' => t('Disabled')), + '#description' => t('Whether to enable click tracking by default or not.'), + '#default_value' => variable_get('mailgun_tracking_clicks', 'default'), + ); + + $form['defaults']['mailgun_tracking_opens'] = array( + '#title' => t('Enable open tracking'), + '#type' => 'select', + '#options' => array('default' => t('Use default setting'), 'enabled' => t('Enabled'), 'disabled' => t('Disabled')), + '#description' => t('Whether to enable open tracking by default or not.'), + '#default_value' => variable_get('mailgun_tracking_opens', 'default'), + ); + } + + $form = system_settings_form($form); + $form['#validate'][] = 'mailgun_admin_settings_validate'; + + return $form; +} + +/** + * Form validation handler for mailgun_admin_settings(). + * + * Perform additional validation to ensure the API key entered is valid. + */ +function mailgun_admin_settings_validate($form, &$form_state) { + if ($form['mailgun_api_key']['#default_value'] != $form_state['values']['mailgun_api_key']) { + // The API key has changed. Perform validation. + $form_state['values']['mailgun_api_key'] = trim($form_state['values']['mailgun_api_key']); + $client = mailgun_get_client($form_state['values']['mailgun_api_key']); + try { + $result = $client->get('domains'); + drupal_set_message(t('Your API key has been successfully validated.')); + } catch (Exception $e) { + form_set_error('mailgun_api_key', t('An exception occurred. @code: @message', array('@code' => $e->getCode(), '@message' => $e->getMessage()))); + } + } +} + +/** + * Form builder. Display a form for sending a test e-mail. + */ +function mailgun_test_form($form, &$form_state) { + drupal_set_title(t('Send test mail')); + + $form['to'] = array( + '#type' => 'textfield', + '#title' => t('To'), + '#default_value' => variable_get('site_mail', ''), + '#description' => t('Type in an address to have the test email sent there.'), + '#required' => TRUE, + ); + + $message = "Howdy!\n\nIf this e-mail is displayed correctly and delivered sound and safe, congrats! You have successfully configured Mailgun."; + $message .= ' Visit the project page to contribute or read documentation to learn more.'; + $message = t($message, array('@project' => url('https://www.drupal.org/project/mailgun'), '@documentation' => url('https://www.drupal.org/node/2547591'))); + $form['message'] = array( + '#type' => 'textarea', + '#title' => t('Message'), + '#default_value' => $message, + '#required' => TRUE, + ); + + $form['attachment'] = array( + '#title' => t('Include attachment'), + '#type' => 'checkbox', + '#description' => t('If checked, the Drupal icon will be included as an attachment with the test e-mail.'), + '#default_value' => TRUE, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Send'), + ); + $form['cancel'] = array( + '#type' => 'link', + '#href' => 'admin/config/system/mailgun', + '#title' => t('Cancel'), + ); + + return $form; +} + +/** + * Form submission handler for mailgun_test_form(). + * Send the test e-mail. + */ +function mailgun_test_form_submit($form, &$form_state) { + $to = $form_state['values']['to']; + $params = array( + 'message' => $form_state['values']['message'], + 'attachment' => $form_state['values']['attachment'], + ); + $site_name = variable_get('site_name', ''); + $default_from = variable_get('site_mail', ini_get('sendmail_from')); + $from = (!empty($site_name)) ? $site_name . ' <' . $default_from . '>' : $default_from; + $result = drupal_mail('mailgun', 'test', $to, $GLOBALS['language'], $params, $from); + drupal_set_message(t('Test email sent from %from to %to. If you have the "Log mails" setting enabled, check the database log for details.', array('%from' => $result['from'], '%to' => $result['to'], '@url' => url('admin/reports/dblog'))), 'status'); +} diff --git a/sites/all/modules/contrib/mail/mailgun/mailgun.info b/sites/all/modules/contrib/mail/mailgun/mailgun.info new file mode 100644 index 00000000..f9822c4a --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/mailgun.info @@ -0,0 +1,16 @@ +name = Mailgun +description = "Provides integration with Mailgun's email sending API." +core = 7.x +package = Mailgun + +dependencies[] = libraries +dependencies[] = mailsystem + +files[] = mailgun.mail.inc + +; Information added by Drupal.org packaging script on 2016-06-26 +version = "7.x-1.6+0-dev" +core = "7.x" +project = "mailgun" +datestamp = "1466914444" + diff --git a/sites/all/modules/contrib/mail/mailgun/mailgun.install b/sites/all/modules/contrib/mail/mailgun/mailgun.install new file mode 100644 index 00000000..52c72700 --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/mailgun.install @@ -0,0 +1,34 @@ + 'MailgunMailSystem')); +} + +/** + * Implements hook_disable(). + */ +function mailgun_disable() { + // Tell Mail System to remove Mailgun and restore to defaults. + mailsystem_clear(array('mailgun_test' => 'MailgunMailSystem')); + watchdog('mailgun', 'Mailgun has been disabled.'); +} diff --git a/sites/all/modules/contrib/mail/mailgun/mailgun.mail.inc b/sites/all/modules/contrib/mail/mailgun/mailgun.mail.inc new file mode 100644 index 00000000..6dbd22f0 --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/mailgun.mail.inc @@ -0,0 +1,123 @@ + $message['from'], + 'to' => $message['to'], + 'subject' => $message['subject'], + 'text' => check_plain($message['body']), + 'html' => $message['body'], + ); + + // Add CC, BCC and Reply-To fields if not empty. + $headers = array_change_key_case($message['headers']); + + if (!empty($headers['cc'])) { + $mailgun_message['cc'] = $headers['cc']; + } + if (!empty($headers['bcc'])) { + $mailgun_message['bcc'] = $headers['bcc']; + } + if (!empty($headers['reply-to'])) { + $mailgun_message['h:Reply-To'] = $headers['reply-to']; + } + + $params = array(); + + // Populate default settings. + if ($variable = variable_get('mailgun_tracking', 'default') != 'default') { + $params['o:tracking'] = $variable; + } + if ($variable = variable_get('mailgun_tracking_clicks', 'default') != 'default') { + $params['o:tracking-clicks'] = $variable; + } + if ($variable = variable_get('mailgun_tracking_opens', 'default') != 'default') { + $params['o:tracking-opens'] = $variable; + } + + // For a full list of allowed parameters, see: https://documentation.mailgun.com/api-sending.html#sending. + $allowed_params = array('o:tag', 'o:campaign', 'o:deliverytime', 'o:dkim', 'o:testmode', 'o:tracking', 'o:tracking-clicks', 'o:tracking-opens'); + foreach ($message['params'] as $key => $value) { + // Check if it's one of the known parameters. + $allowed = (in_array($key, $allowed_params)) ? TRUE : FALSE; + // If more options become available but are not yet supported by the module, uncomment the following line. + //$allowed = (substr($key, 0, 2) == 'o:') ? TRUE : FALSE; + if ($allowed) { + $params[$key] = $value; + } + // Check for custom MIME headers or custom JSON data. + if (substr($key, 0, 2) == 'h:' || substr($key, 0, 2) == 'v:') { + $params[$key] = $value; + } + } + + // Make sure the files provided in the attachments array exist. + if (!empty($message['params']['attachments'])) { + $params['attachments'] = array(); + foreach ($message['params']['attachments'] as $attachment) { + if (file_exists($attachment)) { + $params['attachments'][] = $attachment; + } + } + } + + $mailgun_message['params'] = $params; + + // Queue the message if the setting is enabled. + if (variable_get('mailgun_queue', FALSE)) { + $queue = DrupalQueue::get('mailgun_queue', TRUE); + $queue->createItem($mailgun_message); + return TRUE; + } + + return mailgun_send($mailgun_message); + } + +} diff --git a/sites/all/modules/contrib/mail/mailgun/mailgun.module b/sites/all/modules/contrib/mail/mailgun/mailgun.module new file mode 100644 index 00000000..a5bb037b --- /dev/null +++ b/sites/all/modules/contrib/mail/mailgun/mailgun.module @@ -0,0 +1,261 @@ + 'Mailgun', + 'description' => 'Configure Mailgun settings.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('mailgun_admin_settings'), + 'access arguments' => array('administer mailgun'), + 'file' => 'mailgun.admin.inc', + ); + $items['admin/config/system/mailgun/settings'] = array( + 'title' => 'Settings', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 0, + ); + $items['admin/config/system/mailgun/test'] = array( + 'title' => 'Send test email', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('mailgun_test_form'), + 'access arguments' => array('administer mailgun'), + 'description' => 'Send a test e-mail using the Mailgun API.', + 'file' => 'mailgun.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 1, + ); + + return $items; +} + +/** + * Implements hook_permission(). + */ +function mailgun_permission() { + return array( + 'administer mailgun' => array( + 'title' => t('Administer Mailgun'), + 'description' => t('Perform administration tasks for the Mailgun e-mail sending service.'), + "restrict access" => TRUE, + ), + ); +} + +/** + * Implements hook_help(). + */ +function mailgun_help($path, $arg) { + switch ($path) { + case 'admin/config/system/mailgun': + return '

' . t('See documentation for instructions on installing and configuring Mailgun.', array('@url' => url('https://www.drupal.org/node/2547591'))) . '

'; + break; + case 'admin/config/system/mailgun/test': + return '

' . t('Use this form to send a test e-mail to ensure you have correctly configured Mailgun.') . '

'; + break; + } +} + +/** + * Implements hook_cron_queue_info(). + */ +function mailgun_cron_queue_info() { + $queues = array(); + $queues['mailgun_queue'] = array( + 'worker callback' => 'mailgun_send', + 'time' => 60, + ); + return $queues; +} + +/** + * Implements hook_mail(). + */ +function mailgun_mail($key, &$message, $params) { + switch ($key) { + case 'test': + $message['subject'] = t('Mailgun test email'); + $message['body'] = $params['message']; + if ($params['attachment']) { + $message['params']['attachments'] = array(drupal_realpath('misc/druplicon.png')); + } + break; + } +} + +/** + * Implements hook_libraries_info(). + */ +function mailgun_libraries_info() { + $libraries['mailgun'] = array( + 'name' => 'Mailgun PHP library', + 'vendor url' => 'https://documentation.mailgun.com/wrappers.html#php', + 'download url' => 'https://github.com/mailgun/mailgun-php/archive/v1.7.2.zip', + 'path' => 'vendor', + 'version arguments' => array( + 'file' => 'src/Mailgun/Constants/Constants.php', + // const SDK_VERSION = "1.7"; + 'pattern' => '/const SDK_VERSION = \"((\d+)\.(\d+))\";/', + ), + 'files' => array( + 'php' => array('autoload.php'), + ), + ); + + return $libraries; +} + +/** + * Get the Mailgun client to access Mailgun's endpoints. + * + * @param string $key + * The Mailgun API key. Leave empty to use the API key saved in database. + */ +function mailgun_get_client($key = '') { + // Check if the Mailgun PHP library is installed. + $library = libraries_load('mailgun'); + if (!$library['installed']) { + watchdog('mailgun', 'Mailgun client initialization failed: Unable to load the Mailgun PHP library.', NULL, WATCHDOG_ERROR); + return FALSE; + } + + $key = (empty($key)) ? variable_get('mailgun_api_key', '') : $key; + if (empty($key)) { + watchdog('mailgun', 'Mailgun client initialization failed: Missing API key.', NULL, WATCHDOG_ERROR); + return FALSE; + } + + $client = new \Mailgun\Mailgun($key); + return $client; +} + +/** + * Send an e-mail using the Mailgun API. + * + * @param array $mailgun_message + * A Mailgun message array. Contains the following keys: + * - from: The e-mail addressthe message will be sent from. + * - to: The e-mail addressthe message will be sent to. + * - subject: The subject of the message. + * - text: The plain-text version of the message. Processed using check_plain(). + * - html: The original message content. May contain HTML tags. + * - cc: One or more carbon copy recipients. If multiple, separate with commas. + * - bcc: One or more blind carbon copy recipients. If multiple, separate with commas. + * - o:tag: An array containing the tags to add to the message. See: https://documentation.mailgun.com/user_manual.html#tagging. + * - o:campaign: The campaign ID this message belongs to. See: https://documentation.mailgun.com/user_manual.html#um-campaign-analytics. + * - o:deliverytime: Desired time of delivery. Messages can be scheduled for a maximum of 3 days in the future. See: https://documentation.mailgun.com/api-intro.html#date-format. + * - o:dkim: Boolean indicating whether or not to enable DKIM signatures on per-message basis. + * - o:testmode: Boolean indicating whether or not to enable test mode. See: https://documentation.mailgun.com/user_manual.html#manual-testmode. + * - o:tracking: Boolean indicating whether or not to toggle tracking on a per-message basis. See: https://documentation.mailgun.com/user_manual.html#tracking-messages. + * - o:tracking-clicks: Boolean or string "htmlonly" indicating whether or not to toggle clicks tracking on a per-message basis. Has higher priority than domain-level setting. + * - o:tracking-opens: Boolean indicating whether or not to toggle clicks tracking on a per-message basis. Has higher priority than domain-level setting. + * - h:X-My-Header: h: prefix followed by an arbitrary value allows to append a custom MIME header to the message (X-My-Header in this case). For example, h:Reply-To to specify Reply-To address. + * - v:my-var: v: prefix followed by an arbitrary name allows to attach a custom JSON data to the message. See: https://documentation.mailgun.com/user_manual.html#manual-customdata. + * + * @return bool + * TRUE if the mail was successfully accepted, FALSE otherwise. + */ +function mailgun_send($mailgun_message) { + $client = mailgun_get_client(); + if (!$client) { + return FALSE; + } + + // Test mode + if (variable_get('mailgun_test', FALSE)) { + $mailgun_message['o:testmode'] = 'yes'; + } + + // Merge the $mailgun_message array with options. + $mailgun_message += $mailgun_message['params']; + unset($mailgun_message['params']); + + if (variable_get('mailgun_domain', '_sender') == '_sender') { + // Extract the domain from the sender's email address. Use regular expression to check since it could be either a plain email address or in the form "Name ". + $tokens = (preg_match('/^\s*(.+?)\s*<\s*([^>]+)\s*>$/', $mailgun_message['from'], $matches) === 1) ? explode('@', $matches[2]) : explode('@', $mailgun_message['from']); + $mail_domain = array_pop($tokens); + + // Retrieve a list of available domains first. + $domains = array(); + try { + $result = $client->get('domains'); + if ($result->http_response_code == 200) { + foreach ($result->http_response_body->items as $item) { + $domains[$item->name] = $item->name; + } + } + else { + watchdog('mailgun', 'Mailgun server returned a %code error. Could not retrieve domain list.', array('%code' => $result->http_response_code), WATCHDOG_ERROR); + } + } catch (Exception $e) { + watchdog('mailgun', 'An exception occurred while retrieving domains. @code: @message', array('@code' => $e->getCode(), '@message' => $e->getMessage()), WATCHDOG_ERROR); + } + + if (empty($domains)) { + // No domain available. Although this shouldn't happen, doesn't hurt to check. + return FALSE; + } + + // Now, we need to get the working domain. This is generally the domain the From address is on or the root domain of it. + $working_domain = ''; + if ($key = array_search($mail_domain, $domains) !== FALSE) { + // Great. Found it. + $working_domain = $mail_domain; + } + else { + // Oops. No match. Perhaps it's a subdomain instead. + foreach ($domains as $domain) { + if (strpos($domain, $mail_domain) !== FALSE) { + // Got it. + $working_domain = $domain; + break; + } + } + } + + // There is a chance that the user is attempting to send from an email address that's on a domain not yet added to the Mailgun account. + // In that case, abort sending and report error. + if (empty($working_domain)) { + watchdog('mailgun', 'Unable to locate a working domain for From address %mail. Aborting sending.', array('%mail' => $mailgun_message['from']), WATCHDOG_ERROR); + return FALSE; + } + } + else { + $working_domain = variable_get('mailgun_domain', ''); + } + + // Attachments + $post_data = array(); + if (!empty($mailgun_message['attachments'])) { + // Send message with attachments. + $post_data['attachment'] = $mailgun_message['attachments']; + unset($mailgun_message['attachments']); + } + + try { + $result = $client->sendMessage($working_domain, $mailgun_message, $post_data); + + // For a list of HTTP response codes, see: https://documentation.mailgun.com/api-intro.html#errors. + if ($result->http_response_code == 200) { + if (variable_get('mailgun_log', FALSE)) { + watchdog('mailgun', 'Successfully sent message from %from to %to. %code: %message.', array('%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '%code' => $result->http_response_code, '%message' => $result->http_response_body->message)); + } + return TRUE; + } + else { + watchdog('mailgun', 'Failed to send message from %from to %to. %code: %message.', array('%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '%code' => $result->http_response_code, '%message' => $result->http_response_body->message), WATCHDOG_ERROR); + return FALSE; + } + } catch (Exception $e) { + watchdog('mailgun', 'Exception occurred while trying to send test email from %from to %to. @code: @message.', array('%from' => $mailgun_message['from'], '%to' => $mailgun_message['to'], '@code' => $e->getCode(), '@message' => $e->getMessage())); + } +}