Bird (antes MessageBird) Blog | Creación de un sistema de archivo de correo electrónico: Almacenamiento del cuerpo del correo electrónico

Creación de un sistema de archivo de correo electrónico: Almacenamiento del cuerpo del correo electrónico

Creación de un sistema de archivo de correo electrónico: Almacenamiento del cuerpo del correo electrónico

Creación de un sistema de archivo de correo electrónico: Almacenamiento del cuerpo del correo electrónico

Mar 4, 2019

Publicado por

Publicado por

Bird

Bird

-

Categoría:

Categoría:

Email

Email

Ready to see Bird
in action?

Ready to see Bird
in action?

Building an Email Archiving System: Storing the Email Body

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. En code for this project can be found in the following GitHub repository: https://github.com/jeff-goldstein/PHPArchivePlatform.


Aunque utilizaré S3 y MySQL en este proyecto, no son en absoluto las únicas tecnologías que se pueden utilizar para crear una plataforma de archivado, pero dada su ubicuidad, pensé que eran una buena opción para este proyecto. En un sistema de gran volumen a gran escala utilizaría una base de datos de mayor rendimiento que MySQL, pero para este proyecto de muestra, MySQL es perfecto.


I have detailed below, the steps I took in this primera fase of the project:

  1. Crear el duplicado del correo electrónico para archivarlo

  2. Utilice las funciones de archivo y retransmisión entrante de SparkPost para enviar una copia del correo electrónico original de vuelta a SparkPost para su procesamiento en una estructura JSON y, a continuación, enviarla a un recopilador webhook (aplicación).

  3. Desmontar la estructura JSON para obtener los componentes necesarios

  4. Enviar el cuerpo del correo electrónico a S3 para su almacenamiento

  5. Registrar una entrada en MySQL para cada correo electrónico para referencias cruzadas.


Crear un duplicado del correo electrónico

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. En SparkPost documentation defines the Archive feature in the following way:

Los destinatarios de la lista de archivo recibirán una réplica exacta del mensaje enviado a la dirección RCPT TO. En particular, cualquier enlace codificado destinado al destinatario de RCPT TO será idéntico en los mensajes de archivo.

La única diferencia entre esta copia de archivo y el mensaje original de RCPT TO es que algunas de las cabeceras serán diferentes, ya que la dirección de destino del mensaje de archivo es distinta, pero el cuerpo del mensaje será una réplica exacta.

If you want a deeper explanation, here is a link a 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.

Hay una advertencia a este enfoque; mientras que toda la información del evento en el correo electrónico original está vinculada tanto por un transmission_id como por un message_id, no hay información en el evento de retransmisión de entrada (el mecanismo para obtener y difundir el correo electrónico de archivo) para el correo electrónico duplicado que se vincule a uno de esos dos identificadores y por lo tanto la información para el correo electrónico original. Esto significa que tenemos que colocar los datos en el cuerpo del correo electrónico y el encabezado del correo electrónico original como una manera de unir todos los datos SparkPost del correo electrónico original y de archivo.

Para crear el código que se coloca en el cuerpo del correo electrónico, utilicé el siguiente proceso en la aplicación de creación de correos electrónicos.

  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);

    He aquí un ejemplo:

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

  3. A continuación, me aseguré de añadir el $UID al bloque meta_data de la cabecera X-MSYS-API. Este paso asegura que el UID se incrusta en cada salida de evento para el correo electrónico 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>" } }

Ahora tenemos una forma de vincular todos los datos del correo electrónico original al cuerpo del correo electrónico del archivo.


Obtener la versión de archivo

Para obtener una copia de un correo electrónico para archivarlo, debes seguir los siguientes pasos:

  1. Cree un subdominio al que enviará todos los correos electrónicos de archivo (duplicados)

  2. Configure los registros DNS adecuados para que todos los correos electrónicos enviados a ese subdominio se envíen a SparkPost

  3. Crear un dominio de entrada en SparkPost

  4. Crear un webhook de entrada en SparkPost

  5. Crear una aplicación (colector) para recibir el flujo de datos del webhook SparkPost

Los dos enlaces siguientes pueden servirle de guía en este proceso:

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

  2. Also, the blog I wrote last year, Archivar correos electrónicos: Guía práctica para el seguimiento del correo enviado 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.

Obtención del email duplicado en una estructura 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 colector) that accepts the Relé_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;        }    } }

Ahora que tengo los datos, estoy listo para almacenar el cuerpo en S3.


Almacenamiento del correo duplicado en 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/

Lo que voy a hacer es señalar algunos de los ajustes que elegí y que corresponden a un proyecto como éste.

  1. Control de acceso.  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 a 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. Archivar el Archivo. 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, Transición de objetos. 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.

Una vez que tengo el cubo de S3 creado y mi configuración en su lugar, S3 está listo para que suba el correo electrónico compatible con rfc822 que obtuve del flujo de datos de SparkPost Relay Webhook. Pero antes de subir la carga de correo electrónico rfc822 a S3 tengo que crear un nombre de archivo único que voy a utilizar para almacenar ese correo electrónico.

Para el nombre de archivo único, voy a buscar en el cuerpo del correo electrónico el id oculto que la aplicación remitente colocó en el correo electrónico y usaré ese id como nombre del archivo. Hay formas más elegantes de extraer el connectorId del cuerpo html, pero por simplicidad y claridad voy a usar el siguiente código:

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

* estamos asumiendo que $inputField contiene el valor "ArchiveCode" y fue encontrado en mi archivo config.php.

Con el UID, podemos entonces hacer el nombre de archivo que se utilizará en S3:

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

Ahora puedo abrir mi conexión a S3 y subir el archivo. Si miras el archivo s3.php en el repositorio de GitHub verás que se necesita muy poco código para subir el archivo.

Mi último paso es registrar esta entrada en la tabla MYSQL.


Almacenamiento de metadatos en 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:

  • Una entrada de campo automatizada para la fecha/hora

  • La dirección de correo electrónico de destino (RCPT_TO)

  • La fecha y hora de la cabecera DATE del correo electrónico

  • La cabecera SUBJECT

  • El encabezado de la dirección de correo electrónico FROM

  • El directorio utilizado en el bucket S3

  • El nombre de archivo S3 para el correo electrónico archivado

La función llamada MySQLLog dentro del archivo de aplicación upload.php sigue los pasos necesarios para abrir el enlace a MySQL, inyectar la nueva fila, probar los resultados y cerrar el enlace. Agrego un paso más para una buena medida y es registrar estos datos en un archivo de texto. ¿Debería hacer más registros de errores? Sí. Pero quiero mantener este código ligero para permitir que se ejecute extremadamente rápido. A veces este código será llamado cientos de veces por minuto y necesita ser tan eficiente como sea posible. En futuras actualizaciones, añadiré código auxiliar que procesará los fallos y los enviará por correo electrónico a un administrador para su supervisión.

Para terminar

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.

En futuras revisiones de este proyecto esperaría:

  1. Almacenar todos los eventos de registro del correo electrónico original

  2. Enviar errores de almacenamiento a un administrador cuando se produzca un fallo en la carga o en el registro.

  3. Minimizar la complejidad del colector.

  4. Añadir una interfaz de usuario para ver todos los datos

  5. Posibilidad de reenviar el correo electrónico

Mientras tanto, espero que este proyecto le haya resultado interesante y útil; feliz envío.

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

The right message -> to the right person -> en el right time.

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

The right message -> to the right person -> en el right time.