<?php
namespace GOSMTP\mailer\amazonses;
use GOSMTP\mailer\amazonses\EmailServiceRequest;
/**
*
* Copyright (c) 2014, Daniel Zahariev.
* Copyright (c) 2011, Dan Myers.
* Parts copyright (c) 2008, Donovan Schonknecht.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This is a modified BSD license (the third clause has been removed).
* The BSD license may be found here:
* http://www.opensource.org/licenses/bsd-license.php
*
* Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates.
*
* EmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here:
* http://undesigned.org.za/2007/10/22/amazon-s3-php-class
*
* @copyright 2014 Daniel Zahariev
* @copyright 2011 Dan Myers
* @copyright 2008 Donovan Schonknecht
*/
/**
* EmailService PHP class
*
* @link https://github.com/daniel-zahariev/php-aws-ses
* @package AmazonEmailService
* @version v0.9.1
*/
class EmailService
{
/**
* @link(AWS SES regions, http://docs.aws.amazon.com/ses/latest/DeveloperGuide/regions.html)
*/
const AWS_US_EAST_1 = 'email.us-east-1.amazonaws.com';
const AWS_US_WEST_2 = 'email.us-west-2.amazonaws.com';
const AWS_EU_WEST1 = 'email.eu-west-1.amazonaws.com';
const REQUEST_SIGNATURE_V3 = 'v3';
const REQUEST_SIGNATURE_V4 = 'v4';
/**
* AWS SES Target host of region
*/
protected $__host;
/**
* AWS SES Access key
*/
protected $__accessKey;
/**
* AWS Secret key
*/
protected $__secretKey;
/**
* Enable/disable
*/
protected $__trigger_errors;
/**
* Controls the reuse of CURL hander for sending a bulk of messages
* @deprecated
*/
protected $__bulk_sending_mode = false;
/**
* Optionally reusable EmailServiceRequest instance
*/
protected $__ses_request = null;
/**
* Controls CURLOPT_SSL_VERIFYHOST setting for EmailServiceRequest's curl handler
*/
protected $__verifyHost = true;
/**
* Controls CURLOPT_SSL_VERIFYPEER setting for EmailServiceRequest's curl handler
*/
protected $__verifyPeer = true;
/**
* @var string HTTP Request signature version
*/
protected $__requestSignatureVersion;
/**
* Constructor
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @param string $host Amazon Host through which to send the emails
* @param boolean $trigger_errors Trigger PHP errors when AWS SES API returns an error
* @param string $requestSignatureVersion Version of the request signature
*/
public function __construct($accessKey = null, $secretKey = null, $host = self::AWS_US_EAST_1, $trigger_errors = true, $requestSignatureVersion = self::REQUEST_SIGNATURE_V4)
{
if ($accessKey !== null && $secretKey !== null) {
$this->setAuth($accessKey, $secretKey);
}
$this->__host = $host;
$this->__trigger_errors = $trigger_errors;
$this->__requestSignatureVersion = $requestSignatureVersion;
}
/**
* Set the request signature version
*
* @param string $requestSignatureVersion
* @return EmailService $this
*/
public function setRequestSignatureVersion($requestSignatureVersion)
{
$this->__requestSignatureVersion = $requestSignatureVersion;
return $this;
}
/**
* @return string
*/
public function getRequestSignatureVersion()
{
return $this->__requestSignatureVersion;
}
/**
* Set AWS access key and secret key
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return EmailService $this
*/
public function setAuth($accessKey, $secretKey)
{
$this->__accessKey = $accessKey;
$this->__secretKey = $secretKey;
return $this;
}
/**
* Set AWS Host
* @param string $host AWS Host
*/
public function setHost($host = self::AWS_US_EAST_1)
{
$this->__host = $host;
return $this;
}
/**
* @deprecated
*/
public function enableVerifyHost($enable = true)
{
$this->__verifyHost = (bool)$enable;
return $this;
}
/**
* @deprecated
*/
public function enableVerifyPeer($enable = true)
{
$this->__verifyPeer = (bool)$enable;
return $this;
}
/**
* @deprecated
*/
public function verifyHost()
{
return $this->__verifyHost;
}
/**
* @deprecated
*/
public function verifyPeer()
{
return $this->__verifyPeer;
}
/**
* Get AWS target host
* @return boolean
*/
public function getHost()
{
return $this->__host;
}
/**
* Get AWS SES auth access key
* @return string
*/
public function getAccessKey()
{
return $this->__accessKey;
}
/**
* Get AWS SES auth secret key
* @return string
*/
public function getSecretKey()
{
return $this->__secretKey;
}
/**
* Get the verify peer CURL mode
* @return boolean
*/
public function getVerifyPeer()
{
return $this->__verifyPeer;
}
/**
* Get the verify host CURL mode
* @return boolean
*/
public function getVerifyHost()
{
return $this->__verifyHost;
}
/**
* Get bulk email sending mode
* @return boolean
* @deprecated
*/
public function getBulkMode()
{
return $this->__bulk_sending_mode;
}
/**
* Enable/disable CURLOPT_SSL_VERIFYHOST for EmailServiceRequest's curl handler
* verifyHost and verifyPeer determine whether curl verifies ssl certificates.
* It may be necessary to disable these checks on certain systems.
* These only have an effect if SSL is enabled.
*
* @param boolean $enable New status for the mode
* @return EmailService $this
*/
public function setVerifyHost($enable = true)
{
$this->__verifyHost = (bool)$enable;
return $this;
}
/**
* Enable/disable CURLOPT_SSL_VERIFYPEER for EmailServiceRequest's curl handler
* verifyHost and verifyPeer determine whether curl verifies ssl certificates.
* It may be necessary to disable these checks on certain systems.
* These only have an effect if SSL is enabled.
*
* @param boolean $enable New status for the mode
* @return EmailService $this
*/
public function setVerifyPeer($enable = true)
{
$this->__verifyPeer = (bool)$enable;
return $this;
}
/**
* Enable/disable bulk email sending mode
*
* @param boolean $enable New status for the mode
* @return EmailService $this
* @deprecated
*/
public function setBulkMode($enable = true)
{
$this->__bulk_sending_mode = (bool)$enable;
return $this;
}
/**
* Lists the email addresses that have been verified and can be used as the 'From' address
*
* @return array An array containing two items: a list of verified email addresses, and the request id.
*/
public function listVerifiedEmailAddresses()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'ListIdentities');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('ListIdentities', $ses_response->error);
return [];
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$addresses = array();
foreach ($ses_response->body->ListIdentitiesResult->Identities->member as $address) {
if(is_email($address)) {
$addresses[] = (string)$address;
}
}
$response['Addresses'] = $addresses;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Requests verification of the provided email address, so it can be used
* as the 'From' address when sending emails through EmailService.
*
* After submitting this request, you should receive a verification email
* from Amazon at the specified address containing instructions to follow.
*
* @param string $email The email address to get verified
* @return array The request id for this request.
*/
public function verifyEmailAddress($email)
{
$ses_request = $this->getRequestHandler('POST');
$ses_request->setParameter('Action', 'VerifyEmailAddress');
$ses_request->setParameter('EmailAddress', $email);
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('verifyEmailAddress', $ses_response->error);
return false;
}
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Removes the specified email address from the list of verified addresses.
*
* @param string $email The email address to remove
* @return array The request id for this request.
*/
public function deleteVerifiedEmailAddress($email)
{
$ses_request = $this->getRequestHandler('DELETE');
$ses_request->setParameter('Action', 'DeleteVerifiedEmailAddress');
$ses_request->setParameter('EmailAddress', $email);
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('deleteVerifiedEmailAddress', $ses_response->error);
return false;
}
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves information on the current activity limits for this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html
*
* @return array An array containing information on this account's activity limits.
*/
public function getSendQuota()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'GetSendQuota');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('getSendQuota', $ses_response->error);
return false;
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$response['Max24HourSend'] = (string)$ses_response->body->GetSendQuotaResult->Max24HourSend;
$response['MaxSendRate'] = (string)$ses_response->body->GetSendQuotaResult->MaxSendRate;
$response['SentLast24Hours'] = (string)$ses_response->body->GetSendQuotaResult->SentLast24Hours;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves statistics for the last two weeks of activity on this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html
*
* @return array An array of activity statistics. Each array item covers a 15-minute period.
*/
public function getSendStatistics()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'GetSendStatistics');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('getSendStatistics', $ses_response->error);
return false;
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$datapoints = array();
foreach ($ses_response->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) {
$p = array();
$p['Bounces'] = (string)$datapoint->Bounces;
$p['Complaints'] = (string)$datapoint->Complaints;
$p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts;
$p['Rejects'] = (string)$datapoint->Rejects;
$p['Timestamp'] = (string)$datapoint->Timestamp;
$datapoints[] = $p;
}
$response['SendDataPoints'] = $datapoints;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Given a EmailServiceMessage object, submits the message to the service for sending.
*
* @param EmailServiceMessage $sesMessage An instance of the message class
* @param boolean $use_raw_request If this is true or there are attachments to the email `SendRawEmail` call will be used
* @param boolean $trigger_error Optionally overwrite the class setting for triggering an error (with type check to true/false)
* @return array An array containing the unique identifier for this message and a separate request id.
* Returns false if the provided message is missing any required fields.
* @link(AWS SES Response formats, http://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-responses.html)
*/
public function sendEmail($sesMessage, $use_raw_request = false, $trigger_error = null)
{
if (!$sesMessage->validate()) {
$this->__triggerError('sendEmail', 'Message failed validation.');
return false;
}
$ses_request = $this->getRequestHandler('POST');
$action = !empty($sesMessage->attachments) || $use_raw_request ? 'SendRawEmail' : 'SendEmail';
$ses_request->setParameter('Action', $action);
// Works with both calls
if (!is_null($sesMessage->configuration_set)) {
$ses_request->setParameter('ConfigurationSetName', $sesMessage->configuration_set);
}
if ($action == 'SendRawEmail') {
// https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html
$ses_request->setParameter('RawMessage.Data', $sesMessage->mime);
} else {
$i = 1;
foreach ($sesMessage->to as $to) {
$ses_request->setParameter('Destination.ToAddresses.member.' . $i, $sesMessage->encodeRecipients($to));
$i++;
}
if (is_array($sesMessage->cc)) {
$i = 1;
foreach ($sesMessage->cc as $cc) {
$ses_request->setParameter('Destination.CcAddresses.member.' . $i, $sesMessage->encodeRecipients($cc));
$i++;
}
}
if (is_array($sesMessage->bcc)) {
$i = 1;
foreach ($sesMessage->bcc as $bcc) {
$ses_request->setParameter('Destination.BccAddresses.member.' . $i, $sesMessage->encodeRecipients($bcc));
$i++;
}
}
if (is_array($sesMessage->replyto)) {
$i = 1;
foreach ($sesMessage->replyto as $replyto) {
$ses_request->setParameter('ReplyToAddresses.member.' . $i, $sesMessage->encodeRecipients($replyto));
$i++;
}
}
$ses_request->setParameter('Source', $sesMessage->encodeRecipients($sesMessage->from));
if ($sesMessage->returnpath != null) {
$ses_request->setParameter('ReturnPath', $sesMessage->returnpath);
}
if ($sesMessage->subject != null && strlen($sesMessage->subject) > 0) {
$ses_request->setParameter('Message.Subject.Data', $sesMessage->subject);
if ($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) {
$ses_request->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset);
}
}
if ($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) {
$ses_request->setParameter('Message.Body.Text.Data', $sesMessage->messagetext);
if ($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) {
$ses_request->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset);
}
}
if ($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) {
$ses_request->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml);
if ($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) {
$ses_request->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset);
}
}
$i = 1;
foreach ($sesMessage->message_tags as $key => $value) {
$ses_request->setParameter('Tags.member.' . $i . '.Name', $key);
$ses_request->setParameter('Tags.member.' . $i . '.Value', $value);
$i++;
}
}
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$response = array(
'code' => $ses_response->code,
'error' => array('Error' => array('message' => 'Unexpected HTTP status')),
);
return $response;
}
if ($ses_response->error !== false) {
if (($this->__trigger_errors && ($trigger_error !== false)) || $trigger_error === true) {
$this->__triggerError('sendEmail', $ses_response->error);
return false;
}
return $ses_response;
}
$response = array(
'MessageId' => (string)$ses_response->body->{"{$action}Result"}->MessageId,
'RequestId' => (string)$ses_response->body->ResponseMetadata->RequestId,
);
return $response;
}
public function sendRawEmail($sesMessage)
{
$ses_request = $this->getRequestHandler('POST');
$ses_request->setParameter('Action', 'SendRawEmail');
// https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html
$ses_request->setParameter('RawMessage.Data', $sesMessage);
$ses_response = $ses_request->getResponse();
if (($ses_response->error === false && $ses_response->code !== 200) || $ses_response->error !== false) {
return new \WP_Error($ses_response->code, $this->getErrorMessage('sendRawEmail', $ses_response->error), $ses_response->error);
}
return array(
'MessageId' => (string)$ses_response->body->SendRawEmailResult->MessageId,
'RequestId' => (string)$ses_response->body->ResponseMetadata->RequestId,
);
}
/**
* Trigger an error message
*
* {@internal Used by member functions to output errors}
* @param string $functionname The name of the function that failed
* @param array $error Array containing error information
* @return void
*/
public function __triggerError($functionname, $error)
{
trigger_error($this->getErrorMessage($functionname, $error), E_USER_WARNING);
}
public function getErrorMessage($functionname, $error)
{
if ($error == false) {
return sprintf("EmailService::%s(): Encountered an error, but no description given", $functionname);
} else if (isset($error['curl']) && $error['curl']) {
return sprintf("EmailService::%s(): %s %s", $functionname, $error['code'], $error['message']);
} else if (isset($error['Error'])) {
$e = $error['Error'];
return sprintf("EmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']);
}
return sprintf("EmailService::%s(): Encountered an error: %s", $functionname, $error);
}
/**
* Set SES Request
*
* @param EmailServiceRequest $ses_request description
* @return EmailService $this
*/
public function setRequestHandler(EmailServiceRequest $ses_request = null)
{
if (!is_null($ses_request)) {
$ses_request->setSES($this);
}
$this->__ses_request = $ses_request;
return $this;
}
/**
* Get SES Request
*
* @param string $verb HTTP Verb: GET, POST, DELETE
* @return EmailServiceRequest SES Request
*/
public function getRequestHandler($verb)
{
if (empty($this->__ses_request)) {
$this->__ses_request = new EmailServiceRequest($this, $verb);
} else {
$this->__ses_request->setVerb($verb);
}
return $this->__ses_request;
}
}