Bird (anciennement MessageBird) Blog | Construire un système d'archivage d'emails : Stocker le corps de l'email

Construire un système d'archivage des e-mails : Stockage du corps de l'e-mail

Construire un système d'archivage des e-mails : Stockage du corps de l'e-mail

Building an Courriel : Archiving System: Storing the Courriel : Body

Mar 4, 2019

Publié par

Publié par

Bird

Bird

-

Catégorie :

Catégorie :

Email

Email

Ready to see Bird
in action?

Ready to see Bird
in action?

Construire un système d'archivage des e-mails : Stockage du corps de l'e-mail

In this blog, I will describe the process I went through to store the body of the email onto S3 (Amazon’s Simple Store Service) and ancillary data into a MySQL table for easy cross-referencing. Ultimately, this is the starting point for the code base that will include an application that will allow for easy searching of archived emails, and then displaying those emails along with the event (log) data. Le code for this project can be found in the following GitHub repository: https://github.com/jeff-goldstein/PHPArchivePlatform.


Bien que je m'appuie sur S3 et MySQL dans ce projet, il ne s'agit en aucun cas des seules technologies pouvant être utilisées pour construire une plateforme d'archivage, mais étant donné leur omniprésence, j'ai pensé qu'elles constituaient un bon choix pour ce projet. Dans un système complet à haut volume, j'utiliserais une base de données plus performante que MySQL, mais pour cet exemple de projet, MySQL est parfait.


I have detailed below, the steps I took in this première phase of the project:

  1. Création du double de l'e-mail pour l'archivage

  2. Utilisez les fonctions d'archivage et de relais entrant de SparkPost pour renvoyer une copie de l'e-mail original à SparkPost afin qu'il soit traité dans une structure JSON, puis envoyé à un collecteur de webhook (application).

  3. Démonter la structure JSON pour obtenir les composants nécessaires

  4. Envoyer le corps de l'email à S3 pour le stockage

  5. Enregistrez une entrée dans MySQL pour chaque courriel afin d'établir des références croisées.


Créer un duplicata de l'email

In SparkPost the best way to archive an email is to create an identical copy of the email specifically designed for archival purposes. This is done by using SparkPost’s Archive feature. SparkPost’s Archive feature gives the sender the ability to send a duplicate of the email to one or more email address.  This duplicate uses the same tracking and open links as the original. Le SparkPost documentation defines the Archive feature in the following way:

Les destinataires de la liste d'archives recevront une réplique exacte du message qui a été envoyé à l'adresse RCPT TO. En particulier, tous les liens codés destinés au destinataire du RCPT TO seront identiques dans les messages d'archive.

La seule différence entre cette copie d'archive et l'email RCPT TO original est que certains en-têtes seront différents puisque l'adresse cible de l'email d'archivage est différente, mais le corps de l'email sera une réplique exacte !

If you want a deeper explanation, here is a link à la SparkPost documentation on creating duplicate (or archive) copies of an email. Sample X-MSYS-API headers for this project are shown later in this blog.

Il y a un bémol à cette approche ; alors que toutes les informations sur l'événement dans le courriel original sont liées par un transmission_id et un message_id, il n'y a aucune information dans l'événement de relais entrant (le mécanisme pour obtenir et diffuser le courriel d'archive) pour le courriel dupliqué qui soit liée à l'un de ces deux id et donc aux informations du courriel original. Cela signifie que nous devons placer des données dans le corps de l'e-mail et dans l'en-tête de l'e-mail original afin de relier toutes les données SparkPost de l'e-mail original et de l'e-mail d'archive.

Afin de créer le code qui est placé dans le corps de l'email, j'ai utilisé le processus suivant dans l'application de création d'email.

  1. Somewhere in the email body, I placed the following input entry:<input name="ArchiveCode" type="hidden" value="<<UID>>">

  2. Then I created a unique code and replaced the <<UID>> field:$uid = md5(uniqid(rand(), true)); $emailBody = str_replace(“<<UID>>,$uid,$emailBody);

    Voici un exemple de sortie :

    <input name="ArchiveCode" type="hidden" value="00006365263145">

  3. Ensuite, je me suis assuré d'ajouter le $UID au bloc meta_data de l'en-tête X-MSYS-API. Cette étape permet de s'assurer que l'UID est incorporé dans chaque sortie d'événement pour l'email original :

X-MSYS-API:{ "campaign_id":"<my_campaign>", "metadata":{ "UID":"<UID>" }, "archive":[ { "email":"archive@geekwithapersonality.com" } ], "options":{ "open_tracking":false, "click_tracking":false, "transactional":false, "ip_pool":"<my_ip_pool>" } }

Nous avons maintenant un moyen de lier toutes les données de l'e-mail original au corps de l'e-mail de l'archive.


Obtention de la version d'archive

Afin d'obtenir une copie d'un courriel pour l'archiver, vous devez suivre les étapes suivantes :

  1. Créez un sous-domaine vers lequel vous enverrez tous les e-mails d'archive (dupliqués).

  2. Définissez les enregistrements DNS appropriés pour que tous les e-mails envoyés à ce sous-domaine soient envoyés à SparkPost.

  3. Créer un domaine entrant dans SparkPost

  4. Créer un webhook entrant dans SparkPost

  5. Créer une application (collecteur) pour recevoir le flux de données du webhook SparkPost

Les deux liens suivants peuvent être utilisés pour vous guider dans cette démarche :

  1. SparkPost technical doc: Enabling Inbound Email Relaying & Relay Webhooks

  2. Also, the blog I wrote last year, Archivage des courriels : Un guide pratique pour le suivi des courriers envoyés will walk you through the creation of the inbound relay within SparkPost

* Note: as of Oct 2018, the Archive feature only works when sending emails using an SMTP connection to SparkPost, the RESTful API does not support this feature.  That probably isn’t an issue because most emails that need this level of audit control tend to be personalized emails that are fully built out by a backend application before email delivery is needed.

Obtention du double de l'email dans une structure JSON

In the first phase of this project, all I’m storing is the rfc822 email format in S3 and some high-level description fields into a SQL table for searching.  Since SparkPost will send the email data in a JSON structure to my archiving platform via webhook data streams, I built an application (often referred to as a collecteur) that accepts the Relay_Webhook data stream.

Each package from the SparkPost Relay_Webhook will contain the information of one duplicate email at a time, so breaking the JSON structure down into the targeted components for this project is rather straightforward.  In my PHP code, getting the rfc822 formatted email was as easy as the following few lines of code:

if ($verb == "POST") { $body = file_get_contents("php://input"); $fields = json_decode($body, true); $rfc822body = $fields['0']['msys']['relay_message']['content']['email_rfc822']; $htmlbody = $fields['0']['msys']['relay_message']['content'][html'] $headers = $fields['0']['msys']['relay_message']['content']['headers'];}

Some of the information that I want to store into my SQL table resides in an array of header fields.  So I wrote a small function that accepted the header array and looped through the array in order to obtain the data I was interested in storing:

function get_important_headers($headers, &$original_to, &$headerDate, &$subject, &$from) {    foreach ($headers as $key => $value) {        foreach ($value as $key_sub => $value_sub) {            if ($key_sub == 'To') $original_to = $value_sub;            if ($key_sub == 'Date') $headerDate = $value_sub;            if ($key_sub == 'Subject') $subject = $value_sub;            if ($key_sub == 'From') $from = $value_sub;        }    } }

Maintenant que j'ai les données, je suis prêt à stocker le corps dans S3.


Stockage de l'email dupliqué dans S3

I’m sorry to disappoint you but I’m not going to give a step by step tutorial on creating an S3 bucket for storing the email nor am I going to describe how to create the necessary access key you will need in your application for uploading content to your bucket; there are better tutorials on this subject than I could ever write.  Here a couple of articles that may help:

https://docs.aws.amazon.com/quickstarts/latest/s3backup/step-1-create-bucket.html
https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/

Je vais vous indiquer certains des paramètres que j'ai choisis pour un projet comme celui-ci.

  1. Contrôle d'accès.  You not only need to set the security for the bucket, but you need to set the permissions for the items themselves.  In my project, I use a very open policy of public-read because the sample data is not personal and I wanted easy access à la data.  You will probably want a much stricter set of ACL policies. Here is a nice article on ACL settings:

https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

  2. Archiver l'archive. In S3 there is something called Lifecycle Management.  This allows you to move data from one type of S3 storage class to another.  The different storage classes represent the amount of access you need to the stored data with lower costs associated with the storage you access the least. A good write up of the different classes and transitioning through them can be found in an AWS guide called, Objets de transition. In my case, I chose to create a lifecycle that moved each object from Standard to Glacier after one year. Glacier access is much cheaper than the standard S3 archive and will save me money in storage costs.

Une fois que j'ai créé le seau S3 et que mes paramètres sont en place, S3 est prêt pour que je télécharge l'email conforme à la norme rfc822 que j'ai obtenu à partir du flux de données du Webhook du relais SparkPost. Mais avant de télécharger la charge utile de l'email rfc822 vers S3, je dois créer un nom de fichier unique que j'utiliserai pour stocker cet email.

Pour le nom de fichier unique, je vais rechercher dans le corps de l'email l'identifiant caché que l'application d'envoi a placé dans l'email et utiliser cet identifiant comme nom de fichier. Il existe des moyens plus élégants d'extraire l'identifiant du connecteur du corps du message, mais pour des raisons de simplicité et de clarté, je vais utiliser le code suivant :

       $start = strpos($htmlbody, $inputField);          $start = strpos($htmlbody, "value=", $start) + 7;        $end = strpos($htmlbody, ">", $start) - 1;        $length = $end - $start;        $UID = substr($html, $start, $length);

* Nous supposons que $inputField contient la valeur "ArchiveCode" et a été trouvé dans mon fichier config.php.

Avec l'UID, nous pouvons alors créer le nom de fichier qui sera utilisé dans S3 :

$fileName = $ArchiveDirectory . '/' . $UID . '.eml' ;

Je suis maintenant en mesure d'ouvrir ma connexion à S3 et de télécharger le fichier. Si vous regardez le fichier s3.php dans le dépôt GitHub, vous verrez qu'il faut très peu de code pour télécharger le fichier.

Ma dernière étape consiste à enregistrer cette entrée dans la table MYSQL.


Stockage des métadonnées dans MySQL

We grabbed all of the data necessary in a previous step, so the step of storage is easy.  In this first phase I chose to build a table with the following fields:

  • Un champ de saisie automatisé pour la date et l'heure

  • L'adresse électronique cible (RCPT_TO)

  • L'horodatage de l'en-tête DATE de l'email

  • L'en-tête SUBJECT

  • L'en-tête de l'adresse électronique FROM

  • Le répertoire utilisé dans le seau S3

  • Le nom de fichier S3 pour l'email archivé.

La fonction nommée MySQLLog dans le fichier d'application upload.php effectue les étapes nécessaires pour ouvrir le lien vers MySQL, injecter la nouvelle ligne, tester les résultats et fermer le lien. J'ajoute une autre étape pour faire bonne mesure, à savoir l'enregistrement de ces données dans un fichier texte. Devrais-je faire beaucoup plus de journalisation pour les erreurs ? Oui. Mais je veux garder ce code léger afin de lui permettre de s'exécuter extrêmement rapidement. Parfois, ce code sera appelé des centaines de fois par minute et doit être aussi efficace que possible. Dans les futures mises à jour, j'ajouterai un code auxiliaire qui traitera les échecs et les enverra par courriel à un administrateur pour le suivi.

La conclusion

So in a few fairly easy steps, we were able to walk through the first phase of building a robust email archiving system that holds the email duplicate in S3 and cross-referencing data in a MySQL table.  This will give us a foundation for the rest of the project that will be tackled in several future posts.

Dans les futures révisions de ce projet, je m'attendrais à :

  1. Stocker tous les événements du journal de l'e-mail original

  2. Envoyer des erreurs de stockage à un administrateur lorsqu'un échec de téléchargement ou d'enregistrement se produit.

  3. Minimiser la complexité du collecteur.

  4. Ajouter une interface utilisateur pour visualiser toutes les données

  5. Prendre en charge la possibilité de renvoyer le courriel

En attendant, j'espère que ce projet a été intéressant et utile pour vous ; bon envoi.

Your new standard in Marketing, Pay & Sales. It's Bird

The right message -> to the right person -> au right time.

Your new standard in Marketing, Pay & Sales. It's Bird

The right message -> to the right person -> au right time.