Bird (tidigare MessageBird) Blogggggggg | Bygga ett system för e-postarkivering: Lagring av e-postens brödtext

Bygga ett system för e-postarkivering: Lagring av e-postens brödtext

Bygga ett system för e-postarkivering: Lagring av e-postens brödtext

Building an E-post Archiving System: Storing the E-post Body

Mar 4, 2019

Publicerad av

Publicerad av

Bird

Bird

Kategori:

Kategori:

Email

Email

Ready to see Bird
in action?

Ready to see Bird
in action?

Bygga ett system för e-postarkivering: Lagring av e-postens brödtext

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


Även om jag kommer att använda S3 och MySQL i det här projektet är det inte alls de enda teknikerna som kan användas för att bygga en arkiveringsplattform, men med tanke på att de är så vanliga tyckte jag att de var ett bra val för det här projektet. I ett fullskaligt högvolymssystem skulle jag använda en databas med högre prestanda än MySQL, men för detta exempelprojekt är MySQL perfekt.


I have detailed below, the steps I took in this första fasen of the project:

  1. Skapa ett duplicerat e-postmeddelande för arkivering

  2. Använd SparkPosts funktioner för arkivering och inkommande relä för att skicka en kopia av det ursprungliga e-postmeddelandet tillbaka till SparkPost för bearbetning till en JSON-struktur, som sedan skickas till en webhook-samlare (applikation)

  3. Demontera JSON-strukturen för att få fram de komponenter som behövs

  4. Skicka e-postmeddelandets huvuddel till S3 för lagring

  5. Logga in en post i MySQL för varje e-postmeddelande för korsreferenser


Skapa en duplikat av e-postmeddelandet

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

Mottagare i arkivlistan kommer att få en exakt kopia av det meddelande som skickades till RCPT TO-adressen. I synnerhet kommer alla kodade länkar som är avsedda för RCPT TO-mottagaren att vara identiska i arkivmeddelandena

Den enda skillnaden mellan denna arkivkopia och det ursprungliga RCPT TO-e-postmeddelandet är att vissa av rubrikerna kommer att vara annorlunda eftersom måladressen för det arkiverade e-postmeddelandet är annorlunda, men e-postmeddelandets kropp kommer att vara en exakt kopia!

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

Det finns ett förbehåll för detta tillvägagångssätt; medan all händelseinformation i det ursprungliga e-postmeddelandet binds samman av både ett transmission_id och ett message_id, finns det ingen information i den inkommande relähändelsen (mekanismen för att erhålla och sprida arkivmeddelandet) för duplikatmeddelandet som binder tillbaka till ett av dessa två id:n och därmed informationen för det ursprungliga e-postmeddelandet. Detta innebär att vi måste placera data i e-postens brödtext och rubrik i det ursprungliga e-postmeddelandet som ett sätt att binda samman alla SparkPost-data från det ursprungliga e-postmeddelandet och arkivmeddelandet.

För att skapa koden som placeras i e-posttexten använde jag följande process i programmet för att skapa e-post.

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

    Här är ett exempel på utdata:

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

  3. Därefter såg jag till att $UID lades till i meta_data-blocket i X-MSYS-API-headern. Detta steg säkerställer att UID är inbäddat i varje händelseutmatning för det ursprungliga e-postmeddelandet:

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>" } }

Nu har vi ett sätt att knyta alla data från det ursprungliga e-postmeddelandet till e-posttexten i arkivet.


Hämta arkivversionen

För att få en kopia av ett e-postmeddelande för arkivering måste du vidta följande åtgärder:

  1. Skapa en underdomän som du skickar alla arkiverade (duplicerade) e-postmeddelanden till

  2. Ställ in lämpliga DNS-poster så att alla e-postmeddelanden som skickas till den underdomänen skickas till SparkPost

  3. Skapa en inkommande domän i SparkPost

  4. Skapa en inkommande webhook i SparkPost

  5. Skapa en applikation (samlare) som tar emot dataströmmen från SparkPost webhook

Följande två länkar kan användas för att vägleda dig genom denna process:

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

  2. Also, the blog I wrote last year, Arkivering av e-post: En guide för spårning av skickad e-post 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.

Hämta duplicerad e-post i en JSON-struktur

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

Nu när jag har data är jag redo att lagra kroppen i S3.


Lagring av duplicerat e-postmeddelande i 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/

Vad jag kommer att göra är att peka på några av de inställningar som jag valde och som gäller för ett projekt som detta.

  1. Åtkomstkontroll.  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 till 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. Arkivering av arkivet. 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, Övergång av objekt. 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.

När jag har skapat S3-bucketen och mina inställningar är på plats är S3 redo för mig att ladda upp det rfc822-kompatibla e-postmeddelandet som jag fick från SparkPost Relay Webhook-dataströmmen. Men innan jag laddar upp rfc822-nyttolasten till S3 måste jag skapa ett unikt filnamn som jag kommer att använda för att lagra e-postmeddelandet.

För det unika filnamnet kommer jag att söka i e-posttexten efter det dolda id som den sändande applikationen placerade i e-postmeddelandet och använda det id:t som filnamn. Det finns mer eleganta sätt att hämta connectorId från html-texten, men för enkelhetens och tydlighetens skull kommer jag att använda följande kod:

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

* Vi antar att $inputField har värdet "ArchiveCode" och hittades i min config.php-fil.

Med UID kan vi sedan skapa det filnamn som kommer att användas i S3:

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

Nu kan jag öppna min anslutning till S3 och ladda upp filen. Om du tittar på s3.php-filen i GitHub-arkivet ser du att det krävs väldigt lite kod för att ladda upp filen.

Mitt sista steg är att logga in denna post i MYSQL-tabellen.


Lagring av metadata i 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:

  • En automatiserad fältinmatning för datum/tid

  • Målets e-postadress (RCPT_TO)

  • Tidsstämpel från e-postmeddelandets DATE-rubrik

  • Rubriken SUBJECT

  • E-postadresshuvudet FROM

  • Den katalog som används i S3-skopan

  • S3-filnamn för det arkiverade e-postmeddelandet

Funktionen som heter MySQLLog i upload.php-applikationsfilen går igenom de nödvändiga stegen för att öppna länken till MySQL, injicera den nya raden, testa resultaten och stänga länken. Jag lägger till ytterligare ett steg för säkerhets skull och det är att logga dessa data i en textfil. Borde jag göra mycket mer loggning för fel? Ja, det borde jag. Men jag vill hålla den här koden lite för att göra det möjligt att köra extremt snabbt. Ibland kommer den här koden att anropas hundratals gånger per minut och måste vara så effektiv som möjligt. I framtida uppdateringar kommer jag att lägga till extra kod som bearbetar fel och mejlar dessa fel till en administratör för övervakning.

Avslutning

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.

I framtida revideringar av detta projekt förväntar jag mig att:

  1. Spara alla logghändelser för det ursprungliga e-postmeddelandet

  2. Skicka lagringsfel till en administratör när uppladdning eller loggning misslyckas

  3. Minimera komplexiteten i kollektorn.

  4. Lägg till ett användargränssnitt för visning av alla data

  5. Stöd för möjligheten att skicka e-postmeddelandet igen

Under tiden hoppas jag att detta projekt har varit intressant och användbart för dig; trevlig sändning.

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

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

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

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