From 716ff08b346006ab0c031a9a5f96c1378b9cc180 Mon Sep 17 00:00:00 2001
From: danph <danph@81431.no-reply.drupal.org>
Date: Sun, 29 Jan 2017 12:34:32 -0500
Subject: [PATCH 01/13] Issue #2845675 by dpi: Change composer dependencies to
 official repo

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 3c40286..1e60445 100644
--- a/composer.json
+++ b/composer.json
@@ -27,8 +27,8 @@
     }
   },
   "require": {
-    "drupal/core": "~8.0",
-    "drupal/sms": "~8.0",
+    "drupal/core": "^8.0",
+    "drupal/smsframework": "^1.0@beta",
     "twilio/sdk": "5.4.*"
   }
 }

From dc14ea044f96639356492d85ea0fc6d2830c5c05 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Tue, 11 Oct 2016 17:34:57 -0600
Subject: [PATCH 02/13] Add incoming message support, add dependency

---
 sms_twilio.info.yml              |  2 ++
 src/Plugin/SmsGateway/Twilio.php | 69 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/sms_twilio.info.yml b/sms_twilio.info.yml
index e53166c..7e08c3e 100644
--- a/sms_twilio.info.yml
+++ b/sms_twilio.info.yml
@@ -2,3 +2,5 @@ name: Twilio for SMS Framework
 type: module
 description: 'Integrates Twilio with SMS Framework.'
 core: 8.x
+dependencies:
+  - smsframework:sms
diff --git a/src/Plugin/SmsGateway/Twilio.php b/src/Plugin/SmsGateway/Twilio.php
index 44c0b70..0eb57ef 100644
--- a/src/Plugin/SmsGateway/Twilio.php
+++ b/src/Plugin/SmsGateway/Twilio.php
@@ -3,14 +3,21 @@
 namespace Drupal\sms_twilio\Plugin\SmsGateway;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\sms\Direction;
 use Drupal\sms\Message\SmsDeliveryReport;
+use Drupal\sms\Message\SmsMessage;
 use Drupal\sms\Message\SmsMessageResultStatus;
 use Drupal\sms\Message\SmsMessageReportStatus;
+use Drupal\sms\Message\SmsMessageStatus;
 use Drupal\sms\Plugin\SmsGatewayPluginBase;
 use Drupal\sms\Message\SmsMessageInterface;
 use Drupal\sms\Message\SmsMessageResult;
+use Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface;
+use Symfony\Component\HttpFoundation\Request;
 use Twilio\Exceptions\RestException;
 use Twilio\Rest\Client;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Twilio\Security\RequestValidator;
 
 /**
  * @SmsGateway(
@@ -20,7 +27,7 @@
  *   reports_push = TRUE,
  * )
  */
-class Twilio extends SmsGatewayPluginBase {
+class Twilio extends SmsGatewayPluginBase implements SmsGatewayPluginIncomingInterface {
 
   /**
    * {@inheritdoc}
@@ -145,4 +152,64 @@ public function send(SmsMessageInterface $sms_message) {
     return $result;
   }
 
+  /**
+   * @inheritDoc
+   */
+  public function incoming(SmsMessageInterface $sms_message) {
+    $report = (new SmsDeliveryReport())
+      ->setStatusMessage($sms_message->getOption('data')['SmsStatus']);
+    return (new SmsMessageResult())
+      ->setReports([$report]);
+  }
+
+  private function validate(Request $request) {
+    $token = $this->getConfiguration()['auth_token'];
+    $signature = $request->headers->get('x-twilio-signature');
+    $url = $request->getSchemeAndHttpHost() . $request->getRequestUri();
+    if (!(new RequestValidator($token))->validate($signature, $url, $request->request->all())) {
+      throw new AccessDeniedHttpException();
+    }
+  }
+
+  private function processMedia(array $params) {
+    $i = 0;
+    $files = [];
+    while ($i < $params['NumMedia']) {
+      $files[] = [
+        'url' => $params['MediaUrl' . $i],
+        'content-type' => $params['MediaContentType' . $i],
+      ];
+      $i++;
+    }
+    return $files;
+  }
+
+  /**
+   * Validates the webhook request and creates an SMS message object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *
+   * @throws \Exception
+   *
+   * @return \Drupal\sms\Message\SmsMessage
+   */
+  public function buildIncomingFromRequest(Request $request) {
+    try {
+      $this->validate($request);
+    }
+    catch (\Exception $e) {
+      throw $e;
+    }
+    $params = $request->request->all();
+    $sms = (new SmsMessage())
+      ->setMessage(trim($params['Body']))
+      ->setDirection(Direction::INCOMING)
+      ->setOption('data', $params)
+      ->setSenderNumber($params['From'])
+      ->addRecipients([$params['To']]);
+    if ($files = $this->processMedia($params)) {
+      $sms->setOption('media', $files);
+    }
+    return $sms;
+  }
 }

From b3acdc33ff540e913b347296a04216d2a60b97ac Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Mon, 17 Oct 2016 20:51:00 -1000
Subject: [PATCH 03/13] Changes per PR from maintainer

---
 composer.json                    |  3 ++
 sms_twilio.info.yml              |  2 --
 src/Plugin/SmsGateway/Twilio.php | 74 ++++++++--------------------------------
 src/SmsUtility.php               | 50 +++++++++++++++++++++++++++
 4 files changed, 67 insertions(+), 62 deletions(-)
 create mode 100644 src/SmsUtility.php

diff --git a/composer.json b/composer.json
index 1e60445..0797ca2 100644
--- a/composer.json
+++ b/composer.json
@@ -30,5 +30,8 @@
     "drupal/core": "^8.0",
     "drupal/smsframework": "^1.0@beta",
     "twilio/sdk": "5.4.*"
+  },
+  "suggest": {
+    "drupal/smsframework": "Provides the SmsGateway Plugin annotation."
   }
 }
diff --git a/sms_twilio.info.yml b/sms_twilio.info.yml
index 7e08c3e..e53166c 100644
--- a/sms_twilio.info.yml
+++ b/sms_twilio.info.yml
@@ -2,5 +2,3 @@ name: Twilio for SMS Framework
 type: module
 description: 'Integrates Twilio with SMS Framework.'
 core: 8.x
-dependencies:
-  - smsframework:sms
diff --git a/src/Plugin/SmsGateway/Twilio.php b/src/Plugin/SmsGateway/Twilio.php
index 0eb57ef..01c6718 100644
--- a/src/Plugin/SmsGateway/Twilio.php
+++ b/src/Plugin/SmsGateway/Twilio.php
@@ -3,21 +3,15 @@
 namespace Drupal\sms_twilio\Plugin\SmsGateway;
 
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\sms\Direction;
 use Drupal\sms\Message\SmsDeliveryReport;
-use Drupal\sms\Message\SmsMessage;
 use Drupal\sms\Message\SmsMessageResultStatus;
 use Drupal\sms\Message\SmsMessageReportStatus;
-use Drupal\sms\Message\SmsMessageStatus;
 use Drupal\sms\Plugin\SmsGatewayPluginBase;
 use Drupal\sms\Message\SmsMessageInterface;
 use Drupal\sms\Message\SmsMessageResult;
 use Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface;
-use Symfony\Component\HttpFoundation\Request;
 use Twilio\Exceptions\RestException;
 use Twilio\Rest\Client;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Twilio\Security\RequestValidator;
 
 /**
  * @SmsGateway(
@@ -137,7 +131,8 @@ public function send(SmsMessageInterface $sms_message) {
         // 21408: Account doesn't have the international permission.
         //       (Test recipient: +15005550003)
         $result->setError(SmsMessageResultStatus::ACCOUNT_ERROR);
-        $result->setStatusMessage($message);
+        $report->setStatus(SmsMessageReportStatus::ERROR);
+        $report->setStatusMessage($message);
       }
       else {
         $report->setStatus(SmsMessageReportStatus::ERROR);
@@ -153,63 +148,22 @@ public function send(SmsMessageInterface $sms_message) {
   }
 
   /**
-   * @inheritDoc
+   * Process a SMS message from Twilio.
+   *
+   * @param \Drupal\sms\Message\SmsMessageInterface $sms_message
+   *   A SMS message, with the original Twilio payload optionally set as the
+   *   'data' option on the SmsMessage object.
+   *
+   * @return \Drupal\sms\Message\SmsMessageResult
+   *   The result of receiving the message.
    */
   public function incoming(SmsMessageInterface $sms_message) {
-    $report = (new SmsDeliveryReport())
-      ->setStatusMessage($sms_message->getOption('data')['SmsStatus']);
+    $report = new SmsDeliveryReport();
+    if ($sms_message->getOption('data') && !empty($sms_message->getOption('data')['SmsStatus'])) {
+      $report->setStatusMessage($sms_message->getOption('data')['SmsStatus']);
+    }
     return (new SmsMessageResult())
       ->setReports([$report]);
   }
 
-  private function validate(Request $request) {
-    $token = $this->getConfiguration()['auth_token'];
-    $signature = $request->headers->get('x-twilio-signature');
-    $url = $request->getSchemeAndHttpHost() . $request->getRequestUri();
-    if (!(new RequestValidator($token))->validate($signature, $url, $request->request->all())) {
-      throw new AccessDeniedHttpException();
-    }
-  }
-
-  private function processMedia(array $params) {
-    $i = 0;
-    $files = [];
-    while ($i < $params['NumMedia']) {
-      $files[] = [
-        'url' => $params['MediaUrl' . $i],
-        'content-type' => $params['MediaContentType' . $i],
-      ];
-      $i++;
-    }
-    return $files;
-  }
-
-  /**
-   * Validates the webhook request and creates an SMS message object.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *
-   * @throws \Exception
-   *
-   * @return \Drupal\sms\Message\SmsMessage
-   */
-  public function buildIncomingFromRequest(Request $request) {
-    try {
-      $this->validate($request);
-    }
-    catch (\Exception $e) {
-      throw $e;
-    }
-    $params = $request->request->all();
-    $sms = (new SmsMessage())
-      ->setMessage(trim($params['Body']))
-      ->setDirection(Direction::INCOMING)
-      ->setOption('data', $params)
-      ->setSenderNumber($params['From'])
-      ->addRecipients([$params['To']]);
-    if ($files = $this->processMedia($params)) {
-      $sms->setOption('media', $files);
-    }
-    return $sms;
-  }
 }
diff --git a/src/SmsUtility.php b/src/SmsUtility.php
new file mode 100644
index 0000000..06c6d74
--- /dev/null
+++ b/src/SmsUtility.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\sms_twilio;
+
+use Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Twilio\Security\RequestValidator;
+
+/**
+ * Class SmsUtility
+ * 
+ * Provides utilities for handling Twilio's SMS implementation.
+ * 
+ * @package Drupal\sms_twilio
+ */
+class SmsUtility {
+
+  /**
+   * Validate a request as authentic from Twilio for a given gateway configuration.
+   * 
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   * @param \Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface $plugin
+   * 
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  static public function validate(Request $request, SmsGatewayPluginIncomingInterface $plugin) {
+    /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
+    $token = $plugin->getConfiguration()['auth_token'];
+    $signature = $request->headers->get('x-twilio-signature');
+    $url = $request->getSchemeAndHttpHost() . $request->getRequestUri();
+    if (!(new RequestValidator($token))->validate($signature, $url, $request->request->all())) {
+      throw new AccessDeniedHttpException();
+    }
+  }
+
+  public static function processMedia(array $params) {
+    $i = 0;
+    $files = [];
+    while ($i < $params['NumMedia']) {
+      $files[] = [
+        'url' => $params['MediaUrl' . $i],
+        'content-type' => $params['MediaContentType' . $i],
+      ];
+      $i++;
+    }
+    return $files;
+  }
+
+}

From 9d41725a030674e2a3a6cb41964f8180cf6e1017 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Mon, 17 Oct 2016 20:55:33 -1000
Subject: [PATCH 04/13] Code cleanup and docblock

---
 src/SmsUtility.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/SmsUtility.php b/src/SmsUtility.php
index 06c6d74..b2465bc 100644
--- a/src/SmsUtility.php
+++ b/src/SmsUtility.php
@@ -24,7 +24,7 @@ class SmsUtility {
    * 
    * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
    */
-  static public function validate(Request $request, SmsGatewayPluginIncomingInterface $plugin) {
+  public static function validate(Request $request, SmsGatewayPluginIncomingInterface $plugin) {
     /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
     $token = $plugin->getConfiguration()['auth_token'];
     $signature = $request->headers->get('x-twilio-signature');
@@ -34,6 +34,12 @@ static public function validate(Request $request, SmsGatewayPluginIncomingInterf
     }
   }
 
+  /**
+   * Helper function for processing attached SMS/MMS media.
+   * 
+   * @param array $params The original payload from Twilio.
+   * @return array An array of files, with url and content-type keys.
+   */
   public static function processMedia(array $params) {
     $i = 0;
     $files = [];

From 3e73c798cc7a0378cc843ff7dc97b52bb2f29418 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Tue, 31 Jan 2017 17:06:27 -0700
Subject: [PATCH 05/13] Incoming functionality to match SMS framework API

---
 src/Plugin/SmsGateway/Twilio.php | 69 ++++++++++++++++++++++++++++++++++++++--
 src/SmsUtility.php               | 18 +++++------
 2 files changed, 76 insertions(+), 11 deletions(-)

diff --git a/src/Plugin/SmsGateway/Twilio.php b/src/Plugin/SmsGateway/Twilio.php
index 01c6718..e24f3dd 100644
--- a/src/Plugin/SmsGateway/Twilio.php
+++ b/src/Plugin/SmsGateway/Twilio.php
@@ -3,15 +3,21 @@
 namespace Drupal\sms_twilio\Plugin\SmsGateway;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\sms\Direction;
+use Drupal\sms\Entity\SmsGatewayInterface;
 use Drupal\sms\Message\SmsDeliveryReport;
+use Drupal\sms\Message\SmsMessage;
 use Drupal\sms\Message\SmsMessageResultStatus;
 use Drupal\sms\Message\SmsMessageReportStatus;
 use Drupal\sms\Plugin\SmsGatewayPluginBase;
 use Drupal\sms\Message\SmsMessageInterface;
 use Drupal\sms\Message\SmsMessageResult;
-use Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface;
+use Drupal\sms\SmsProcessingResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Twilio\Exceptions\RestException;
 use Twilio\Rest\Client;
+use Drupal\sms_twilio\SmsUtility;
 
 /**
  * @SmsGateway(
@@ -19,9 +25,11 @@
  *   label = @Translation("Twilio"),
  *   outgoing_message_max_recipients = 1,
  *   reports_push = TRUE,
+ *   incoming = TRUE,
+ *   incoming_route = TRUE
  * )
  */
-class Twilio extends SmsGatewayPluginBase implements SmsGatewayPluginIncomingInterface {
+class Twilio extends SmsGatewayPluginBase {
 
   /**
    * {@inheritdoc}
@@ -166,4 +174,61 @@ public function incoming(SmsMessageInterface $sms_message) {
       ->setReports([$report]);
   }
 
+  /**
+   * Validates the webhook request and creates an SMS message object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *
+   * @return \Drupal\sms\Message\SmsMessage
+   */
+  protected function buildIncomingFromRequest(Request $request) {
+    $result = new SmsMessageResult();
+    $params = $request->request->all();
+    $report = (new SmsDeliveryReport())
+      ->setRecipient($params['To'])
+      ->setStatus(SmsMessageReportStatus::DELIVERED);
+    $sms = (new SmsMessage())
+      ->setMessage(trim($params['Body']))
+      ->setDirection(Direction::INCOMING)
+      ->setOption('data', $params)
+      ->setSenderNumber($params['From'])
+      ->addRecipients([$params['To']]);
+    if ($files = SmsUtility::processMedia($params)) {
+      $sms->setOption('media', $files);
+    }
+    try {
+      SmsUtility::validate($request, $this);
+    } catch (\Exception $e) {
+      $report->setStatus(SmsMessageReportStatus::REJECTED);
+      $report->setStatusMessage($e->getMessage());
+      $result->setError($e->getCode());
+      $result->setErrorMessage($e->getMessage());
+    }
+    $result->setReports([$report]);
+    $sms->setResult($result);
+    return $sms;
+  }
+
+  /**
+   * Callback for processing incoming messages.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   * @param \Drupal\sms\Entity\SmsGatewayInterface $gateway
+   *
+   * @return \Drupal\sms\SmsProcessingResponse
+   */
+  public function processIncoming(Request $request, SmsGatewayInterface $sms_gateway) {
+    $task = new SmsProcessingResponse();
+    $sms = $this->buildIncomingFromRequest($request);
+    $sms->setGateway($sms_gateway);
+    // Replies should be handled in implementing code.
+    $response = new Response();
+    if ($sms->getResult()->getError()) {
+      $response = new Response($sms->getResult()->getErrorMessage(), $sms->getResult()->getError());
+    }
+    $task->setMessages([$sms]);
+    $task->setResponse($response);
+    return $task;
+  }
+
 }
diff --git a/src/SmsUtility.php b/src/SmsUtility.php
index b2465bc..561b46b 100644
--- a/src/SmsUtility.php
+++ b/src/SmsUtility.php
@@ -2,41 +2,41 @@
 
 namespace Drupal\sms_twilio;
 
-use Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface;
+use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Twilio\Security\RequestValidator;
 
 /**
  * Class SmsUtility
- * 
+ *
  * Provides utilities for handling Twilio's SMS implementation.
- * 
+ *
  * @package Drupal\sms_twilio
  */
 class SmsUtility {
 
   /**
    * Validate a request as authentic from Twilio for a given gateway configuration.
-   * 
+   *
    * @param \Symfony\Component\HttpFoundation\Request $request
-   * @param \Drupal\sms\Plugin\SmsGatewayPluginIncomingInterface $plugin
-   * 
+   * @param \Drupal\sms_twilio\Plugin\SmsGateway\Twilio $plugin
+   *
    * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
    */
-  public static function validate(Request $request, SmsGatewayPluginIncomingInterface $plugin) {
+  public static function validate(Request $request, Twilio $plugin) {
     /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
     $token = $plugin->getConfiguration()['auth_token'];
     $signature = $request->headers->get('x-twilio-signature');
     $url = $request->getSchemeAndHttpHost() . $request->getRequestUri();
     if (!(new RequestValidator($token))->validate($signature, $url, $request->request->all())) {
-      throw new AccessDeniedHttpException();
+      throw new AccessDeniedHttpException('Signature validation failed.');
     }
   }
 
   /**
    * Helper function for processing attached SMS/MMS media.
-   * 
+   *
    * @param array $params The original payload from Twilio.
    * @return array An array of files, with url and content-type keys.
    */

From 1d5b7a8e67e2cb24bf7d22e6659d5d8c7b5235e8 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Thu, 2 Feb 2017 14:42:50 -0700
Subject: [PATCH 06/13] Changes from d.o. issue queue

---
 src/Plugin/SmsGateway/Twilio.php        | 62 +++++++++++++++++++++++++++++++++
 src/Plugin/Utility/TwilioMedia.php      | 30 ++++++++++++++++
 src/Plugin/Utility/TwilioValidation.php | 33 ++++++++++++++++++
 3 files changed, 125 insertions(+)
 create mode 100644 src/Plugin/Utility/TwilioMedia.php
 create mode 100644 src/Plugin/Utility/TwilioValidation.php

diff --git a/src/Plugin/SmsGateway/Twilio.php b/src/Plugin/SmsGateway/Twilio.php
index 44c0b70..e0a3ad5 100644
--- a/src/Plugin/SmsGateway/Twilio.php
+++ b/src/Plugin/SmsGateway/Twilio.php
@@ -3,12 +3,20 @@
 namespace Drupal\sms_twilio\Plugin\SmsGateway;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\sms\Direction;
+use Drupal\sms\Entity\SmsGatewayInterface;
 use Drupal\sms\Message\SmsDeliveryReport;
+use Drupal\sms\Message\SmsMessage;
 use Drupal\sms\Message\SmsMessageResultStatus;
 use Drupal\sms\Message\SmsMessageReportStatus;
 use Drupal\sms\Plugin\SmsGatewayPluginBase;
 use Drupal\sms\Message\SmsMessageInterface;
 use Drupal\sms\Message\SmsMessageResult;
+use Drupal\sms\SmsProcessingResponse;
+use Drupal\sms_twilio\Utility\TwilioMedia;
+use Drupal\sms_twilio\Utility\TwilioValidation;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Twilio\Exceptions\RestException;
 use Twilio\Rest\Client;
 
@@ -145,4 +153,58 @@ public function send(SmsMessageInterface $sms_message) {
     return $result;
   }
 
+  /**
+   * Validates the webhook request and creates an SMS message object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *
+   * @return \Drupal\sms\Message\SmsMessage
+   */
+  protected function buildIncomingFromRequest(Request $request) {
+    $result = new SmsMessageResult();
+    $params = $request->request->all();
+    $report = (new SmsDeliveryReport())
+      ->setRecipient($params['To'])
+      ->setStatus(SmsMessageReportStatus::DELIVERED);
+    $sms = (new SmsMessage())
+      ->setMessage(trim($params['Body']))
+      ->setDirection(Direction::INCOMING)
+      ->setOption('data', $params)
+      ->setSenderNumber($params['From'])
+      ->addRecipients([$params['To']]);
+    if ($files = TwilioMedia::processMedia($params)) {
+      $sms->setOption('media', $files);
+    }
+    try {
+      TwilioValidation::validateIncoming($request, $this);
+    } catch (\Exception $e) {
+      $report->setStatus(SmsMessageReportStatus::REJECTED);
+      $report->setStatusMessage($e->getMessage());
+      $result->setError($e->getCode());
+      $result->setErrorMessage($e->getMessage());
+    }
+    $result->setReports([$report]);
+    $sms->setResult($result);
+
+    return $sms;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processIncoming(Request $request, SmsGatewayInterface $sms_gateway) {
+    $task = new SmsProcessingResponse();
+    $sms = $this->buildIncomingFromRequest($request);
+    $sms->setGateway($sms_gateway);
+    $response = new Response();
+    if ($sms->getResult()->getError()) {
+      $response = new Response($sms->getResult()->getErrorMessage(), $sms->getResult()->getError());
+    }
+    $task->setMessages([$sms]);
+    $task->setResponse($response);
+
+    return $task;
+  }
+
 }
diff --git a/src/Plugin/Utility/TwilioMedia.php b/src/Plugin/Utility/TwilioMedia.php
new file mode 100644
index 0000000..e804bae
--- /dev/null
+++ b/src/Plugin/Utility/TwilioMedia.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file contains TwilioMedia.php
+ * Defines methods for dealing with Twilio media
+ */
+
+namespace Drupal\sms_twilio\Utility;
+
+
+class TwilioMedia {
+
+  /**
+   * Helper function for processing attached SMS/MMS media.
+   *
+   * @param array $params The original payload from Twilio.
+   * @return array An array of files, with url and content-type keys.
+   */
+  public static function processMedia(array $params) {
+    $i = 0;
+    $files = [];
+    while ($i < $params['NumMedia']) {
+      $files[] = [
+        'url' => $params['MediaUrl' . $i],
+        'content-type' => $params['MediaContentType' . $i],
+      ];
+      $i++;
+    }
+    return $files;
+  }
+}
diff --git a/src/Plugin/Utility/TwilioValidation.php b/src/Plugin/Utility/TwilioValidation.php
new file mode 100644
index 0000000..2e503b5
--- /dev/null
+++ b/src/Plugin/Utility/TwilioValidation.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * @file contains TwilioValidator.php
+ * Defines methods for validating Twilio messages
+ */
+
+namespace Drupal\sms_twilio\Utility;
+use Drupal\Core\Url;
+use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
+use Symfony\Component\HttpFoundation\Request;
+use Twilio\Security\RequestValidator;
+
+/**
+ * Class TwilioValidator
+ * @package Drupal\sms_twilio\Utility
+ */
+class TwilioValidation {
+  /**
+   * Validate an incoming message using Twilio SDK
+   * @see https://www.twilio.com/docs/api/security
+   */
+  public static function validateIncoming(Request $request, Twilio $sms_gateway) {
+    $url = Url::fromRoute('sms.incoming.receive.twilio')
+      ->setAbsolute()
+      ->toString();
+    $signature = $request->server->get('HTTP_X_TWILIO_SIGNATURE');
+    $token = $sms_gateway->getConfiguration()['auth_token'];
+
+    $validator = new RequestValidator($token);
+
+    return $validator->validate($signature, $url, $_REQUEST);
+  }
+}

From d831728d360ffa47338b80a70e2bb0a4f72f4ff0 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@jones.name>
Date: Thu, 2 Feb 2017 14:53:21 -0700
Subject: [PATCH 07/13] Simplify suggest language in composer.json

---
 composer.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index 0797ca2..7e527e5 100644
--- a/composer.json
+++ b/composer.json
@@ -32,6 +32,6 @@
     "twilio/sdk": "5.4.*"
   },
   "suggest": {
-    "drupal/smsframework": "Provides the SmsGateway Plugin annotation."
+    "drupal/smsframework": "Provides integration with SMS Framework"
   }
 }

From dab69b84e68ac3fc903be22bfc271909a8204864 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 15:58:57 -0700
Subject: [PATCH 08/13] Clean up unused file, don't nest Utility methods under
 the Plugin directory

---
 src/Plugin/Utility/TwilioMedia.php      | 30 ------------------
 src/Plugin/Utility/TwilioValidation.php | 33 -------------------
 src/SmsUtility.php                      | 56 ---------------------------------
 src/Utility/TwilioMedia.php             | 32 +++++++++++++++++++
 src/Utility/TwilioValidation.php        | 33 +++++++++++++++++++
 5 files changed, 65 insertions(+), 119 deletions(-)
 delete mode 100644 src/Plugin/Utility/TwilioMedia.php
 delete mode 100644 src/Plugin/Utility/TwilioValidation.php
 delete mode 100644 src/SmsUtility.php
 create mode 100644 src/Utility/TwilioMedia.php
 create mode 100644 src/Utility/TwilioValidation.php

diff --git a/src/Plugin/Utility/TwilioMedia.php b/src/Plugin/Utility/TwilioMedia.php
deleted file mode 100644
index e804bae..0000000
--- a/src/Plugin/Utility/TwilioMedia.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-/**
- * @file contains TwilioMedia.php
- * Defines methods for dealing with Twilio media
- */
-
-namespace Drupal\sms_twilio\Utility;
-
-
-class TwilioMedia {
-
-  /**
-   * Helper function for processing attached SMS/MMS media.
-   *
-   * @param array $params The original payload from Twilio.
-   * @return array An array of files, with url and content-type keys.
-   */
-  public static function processMedia(array $params) {
-    $i = 0;
-    $files = [];
-    while ($i < $params['NumMedia']) {
-      $files[] = [
-        'url' => $params['MediaUrl' . $i],
-        'content-type' => $params['MediaContentType' . $i],
-      ];
-      $i++;
-    }
-    return $files;
-  }
-}
diff --git a/src/Plugin/Utility/TwilioValidation.php b/src/Plugin/Utility/TwilioValidation.php
deleted file mode 100644
index 2e503b5..0000000
--- a/src/Plugin/Utility/TwilioValidation.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-/**
- * @file contains TwilioValidator.php
- * Defines methods for validating Twilio messages
- */
-
-namespace Drupal\sms_twilio\Utility;
-use Drupal\Core\Url;
-use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
-use Symfony\Component\HttpFoundation\Request;
-use Twilio\Security\RequestValidator;
-
-/**
- * Class TwilioValidator
- * @package Drupal\sms_twilio\Utility
- */
-class TwilioValidation {
-  /**
-   * Validate an incoming message using Twilio SDK
-   * @see https://www.twilio.com/docs/api/security
-   */
-  public static function validateIncoming(Request $request, Twilio $sms_gateway) {
-    $url = Url::fromRoute('sms.incoming.receive.twilio')
-      ->setAbsolute()
-      ->toString();
-    $signature = $request->server->get('HTTP_X_TWILIO_SIGNATURE');
-    $token = $sms_gateway->getConfiguration()['auth_token'];
-
-    $validator = new RequestValidator($token);
-
-    return $validator->validate($signature, $url, $_REQUEST);
-  }
-}
diff --git a/src/SmsUtility.php b/src/SmsUtility.php
deleted file mode 100644
index 561b46b..0000000
--- a/src/SmsUtility.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-namespace Drupal\sms_twilio;
-
-use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Twilio\Security\RequestValidator;
-
-/**
- * Class SmsUtility
- *
- * Provides utilities for handling Twilio's SMS implementation.
- *
- * @package Drupal\sms_twilio
- */
-class SmsUtility {
-
-  /**
-   * Validate a request as authentic from Twilio for a given gateway configuration.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   * @param \Drupal\sms_twilio\Plugin\SmsGateway\Twilio $plugin
-   *
-   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
-   */
-  public static function validate(Request $request, Twilio $plugin) {
-    /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
-    $token = $plugin->getConfiguration()['auth_token'];
-    $signature = $request->headers->get('x-twilio-signature');
-    $url = $request->getSchemeAndHttpHost() . $request->getRequestUri();
-    if (!(new RequestValidator($token))->validate($signature, $url, $request->request->all())) {
-      throw new AccessDeniedHttpException('Signature validation failed.');
-    }
-  }
-
-  /**
-   * Helper function for processing attached SMS/MMS media.
-   *
-   * @param array $params The original payload from Twilio.
-   * @return array An array of files, with url and content-type keys.
-   */
-  public static function processMedia(array $params) {
-    $i = 0;
-    $files = [];
-    while ($i < $params['NumMedia']) {
-      $files[] = [
-        'url' => $params['MediaUrl' . $i],
-        'content-type' => $params['MediaContentType' . $i],
-      ];
-      $i++;
-    }
-    return $files;
-  }
-
-}
diff --git a/src/Utility/TwilioMedia.php b/src/Utility/TwilioMedia.php
new file mode 100644
index 0000000..eab7baa
--- /dev/null
+++ b/src/Utility/TwilioMedia.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\sms_twilio\Utility;
+
+/**
+ * Class TwilioMedia
+ *
+ * Contains methods for working with media sent by SMS.
+ *
+ * @package Drupal\sms_twilio\Utility
+ */
+class TwilioMedia {
+
+  /**
+   * Helper function for processing attached SMS/MMS media.
+   *
+   * @param array $params The original payload from Twilio.
+   * @return array An array of files, with url and content-type keys.
+   */
+  public static function processMedia(array $params) {
+    $i = 0;
+    $files = [];
+    while ($i < $params['NumMedia']) {
+      $files[] = [
+        'url' => $params['MediaUrl' . $i],
+        'content-type' => $params['MediaContentType' . $i],
+      ];
+      $i++;
+    }
+    return $files;
+  }
+}
diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
new file mode 100644
index 0000000..24b0c95
--- /dev/null
+++ b/src/Utility/TwilioValidation.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\sms_twilio\Utility;
+
+use Drupal\Core\Url;
+use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
+use Symfony\Component\HttpFoundation\Request;
+use Twilio\Security\RequestValidator;
+
+/**
+ * Class TwilioValidation
+ *
+ * Methods for validating incoming webhook POST events from Twilio.
+ *
+ * @package Drupal\sms_twilio\Utility
+ */
+class TwilioValidation {
+  /**
+   * Validate an incoming message using Twilio SDK
+   * @see https://www.twilio.com/docs/api/security
+   */
+  public static function validateIncoming(Request $request, Twilio $sms_gateway) {
+    $url = Url::fromRoute('sms.incoming.receive.twilio')
+      ->setAbsolute()
+      ->toString();
+    $signature = $request->server->get('HTTP_X_TWILIO_SIGNATURE');
+    $token = $sms_gateway->getConfiguration()['auth_token'];
+
+    $validator = new RequestValidator($token);
+
+    return $validator->validate($signature, $url, $_REQUEST);
+  }
+}

From dd02c6e012b64c9022ece4f941d1004b176909fa Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 16:08:53 -0700
Subject: [PATCH 09/13] Additional cleanup of the validation logic; no longer
 throws an exception.

---
 src/Plugin/SmsGateway/Twilio.php |  4 +---
 src/Utility/TwilioValidation.php | 11 +++++++++--
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/Plugin/SmsGateway/Twilio.php b/src/Plugin/SmsGateway/Twilio.php
index 4c83584..6b93da5 100644
--- a/src/Plugin/SmsGateway/Twilio.php
+++ b/src/Plugin/SmsGateway/Twilio.php
@@ -178,9 +178,7 @@ protected function buildIncomingFromRequest(Request $request) {
     if ($files = TwilioMedia::processMedia($params)) {
       $sms->setOption('media', $files);
     }
-    try {
-      TwilioValidation::validateIncoming($request, $this);
-    } catch (\Exception $e) {
+    if(!TwilioValidation::validateIncoming($request, $this)) {
       $report->setStatus(SmsMessageReportStatus::REJECTED);
       $report->setStatusMessage($e->getMessage());
       $result->setError($e->getCode());
diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
index 24b0c95..6fd935f 100644
--- a/src/Utility/TwilioValidation.php
+++ b/src/Utility/TwilioValidation.php
@@ -3,9 +3,9 @@
 namespace Drupal\sms_twilio\Utility;
 
 use Drupal\Core\Url;
-use Drupal\sms_twilio\Plugin\SmsGateway\Twilio;
 use Symfony\Component\HttpFoundation\Request;
 use Twilio\Security\RequestValidator;
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
 
 /**
  * Class TwilioValidation
@@ -15,11 +15,17 @@
  * @package Drupal\sms_twilio\Utility
  */
 class TwilioValidation {
+
   /**
    * Validate an incoming message using Twilio SDK
    * @see https://www.twilio.com/docs/api/security
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request The request object.
+   * @param \Drupal\Component\Plugin\ConfigurablePluginInterface The Twilio plugin.
+   *
+   * @return boolean TRUE if the request validates, FALSE if not.
    */
-  public static function validateIncoming(Request $request, Twilio $sms_gateway) {
+  public static function validateIncoming(Request $request, ConfigurablePluginInterface $sms_gateway) {
     $url = Url::fromRoute('sms.incoming.receive.twilio')
       ->setAbsolute()
       ->toString();
@@ -30,4 +36,5 @@ public static function validateIncoming(Request $request, Twilio $sms_gateway) {
 
     return $validator->validate($signature, $url, $_REQUEST);
   }
+
 }

From 0ce0c816eee0050d68d1e922b533181b0c7459e5 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 16:12:21 -0700
Subject: [PATCH 10/13] Avoid use of superglobals

---
 src/Utility/TwilioValidation.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
index 6fd935f..64efea3 100644
--- a/src/Utility/TwilioValidation.php
+++ b/src/Utility/TwilioValidation.php
@@ -34,7 +34,7 @@ public static function validateIncoming(Request $request, ConfigurablePluginInte
 
     $validator = new RequestValidator($token);
 
-    return $validator->validate($signature, $url, $_REQUEST);
+    return $validator->validate($signature, $url, $request->request->all());
   }
 
 }

From 177e9014befc3b44f2ec581ffb077a05c55f8d74 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 16:13:01 -0700
Subject: [PATCH 11/13] Fix header name

---
 src/Utility/TwilioValidation.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
index 64efea3..98a8d50 100644
--- a/src/Utility/TwilioValidation.php
+++ b/src/Utility/TwilioValidation.php
@@ -29,7 +29,7 @@ public static function validateIncoming(Request $request, ConfigurablePluginInte
     $url = Url::fromRoute('sms.incoming.receive.twilio')
       ->setAbsolute()
       ->toString();
-    $signature = $request->server->get('HTTP_X_TWILIO_SIGNATURE');
+    $signature = $request->server->get('x-twilio-signature');
     $token = $sms_gateway->getConfiguration()['auth_token'];
 
     $validator = new RequestValidator($token);

From a80af5dd7cad4427083fbf4d3e58a34bc450b275 Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 16:13:36 -0700
Subject: [PATCH 12/13] Headers live in the headers parameter, just being more
 explicit

---
 src/Utility/TwilioValidation.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
index 98a8d50..e770eb8 100644
--- a/src/Utility/TwilioValidation.php
+++ b/src/Utility/TwilioValidation.php
@@ -29,7 +29,7 @@ public static function validateIncoming(Request $request, ConfigurablePluginInte
     $url = Url::fromRoute('sms.incoming.receive.twilio')
       ->setAbsolute()
       ->toString();
-    $signature = $request->server->get('x-twilio-signature');
+    $signature = $request->headers->get('x-twilio-signature');
     $token = $sms_gateway->getConfiguration()['auth_token'];
 
     $validator = new RequestValidator($token);

From 7cd3f99d498697b2e1640bb1867bbb80ecea951d Mon Sep 17 00:00:00 2001
From: Brad Jones <brad@bradjonesllc.com>
Date: Thu, 2 Feb 2017 16:22:02 -0700
Subject: [PATCH 13/13] The plugin ID is not necessarily 'twilio'.

---
 src/Utility/TwilioValidation.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Utility/TwilioValidation.php b/src/Utility/TwilioValidation.php
index e770eb8..964e1f5 100644
--- a/src/Utility/TwilioValidation.php
+++ b/src/Utility/TwilioValidation.php
@@ -3,9 +3,9 @@
 namespace Drupal\sms_twilio\Utility;
 
 use Drupal\Core\Url;
+use Drupal\sms\Plugin\SmsGatewayPluginInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Twilio\Security\RequestValidator;
-use Drupal\Component\Plugin\ConfigurablePluginInterface;
 
 /**
  * Class TwilioValidation
@@ -25,8 +25,8 @@ class TwilioValidation {
    *
    * @return boolean TRUE if the request validates, FALSE if not.
    */
-  public static function validateIncoming(Request $request, ConfigurablePluginInterface $sms_gateway) {
-    $url = Url::fromRoute('sms.incoming.receive.twilio')
+  public static function validateIncoming(Request $request, SmsGatewayPluginInterface $sms_gateway) {
+    $url = Url::fromRoute('sms.incoming.receive.' . $sms_gateway->getPluginId())
       ->setAbsolute()
       ->toString();
     $signature = $request->headers->get('x-twilio-signature');
