<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrew B. Collier</title>
    <description>The latest articles on DEV Community by Andrew B. Collier (@datawookie).</description>
    <link>https://dev.to/datawookie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F590333%2F2d21556e-94d2-48d3-aa58-e6b6beaa00cc.jpg</url>
      <title>DEV Community: Andrew B. Collier</title>
      <link>https://dev.to/datawookie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/datawookie"/>
    <language>en</language>
    <item>
      <title>HCRIS Field Labels</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Tue, 19 Oct 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/hcris-field-labels-47d2</link>
      <guid>https://dev.to/datawookie/hcris-field-labels-47d2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pRCy_aAw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/hcris-field-labels/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pRCy_aAw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/hcris-field-labels/featured.jpg" alt="HCRIS Field Labels"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.fathomdata.dev"&gt;Fathom Data&lt;/a&gt; has been doing a lot of work with the &lt;a href="https://www.nber.org/research/data/healthcare-cost-report-information-system-hcris"&gt;HCRIS (Healthcare Cost Report Information System) data&lt;/a&gt;. The underlying reports are submitted as a spreadsheet with multiple sheets. The data are then extracted and recorded in a simple tabular format, with each field linked to a worksheet code (&lt;code&gt;wksht_cd&lt;/code&gt;), column number (&lt;code&gt;clmn_num&lt;/code&gt;) and line number (&lt;code&gt;clmn_num&lt;/code&gt;). These three keys are then mapped to a single compound key. The resulting data look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       key value
--------------------------
   a_c1_30 8960498
   a_c2_30 2559809
   a_c6_30 -1581948
  a_c1_200 45448537
  a_c2_200 100473288
  a_c6_200 -23947062
 a7_1_c1_1 2696198
 a7_1_c1_6 87655935
 a7_1_c2_6 4190056
 a7_1_c1_8 168428817
 a7_1_c2_8 4391574
a7_1_c1_10 168428817
a7_1_c2_10 4391574
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, for these data to make any sense at all it’s necessary to map backwards from the compound key to the location within the original spreadsheet. And once you know &lt;em&gt;where&lt;/em&gt; the data belong, you’ll also want to know &lt;em&gt;what&lt;/em&gt; the data mean.&lt;/p&gt;

&lt;p&gt;Fortunately this relationship is documented and available as a &lt;a href="//HOSP2010_SAS_FILE_RECORD_LAYOUT.pdf"&gt;PDF file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8IRLCY3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/hcris-field-labels/record-layout.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8IRLCY3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/hcris-field-labels/record-layout.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is useful, but for analytical purposes a PDF is not ideal. We used the &lt;a href="https://cran.r-project.org/package=pdftools"&gt;&lt;code&gt;{pdftools}&lt;/code&gt;&lt;/a&gt; package to scrape the text from the PDF and, after a significant bit of wrangling ended up with a nice clean CSV file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;head(hcris_fields)

# A tibble: 6 × 6
  key type wksht_cd clmn_num line_num label                 
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt;                 
1 a7_1_c1_1 NUMERIC A700001 00100 00100 Land                  
2 a7_1_c1_10 NUMERIC A700001 00100 01000 Total                 
3 a7_1_c1_2 NUMERIC A700001 00100 00200 Land Improvements     
4 a7_1_c1_3 NUMERIC A700001 00100 00300 Buildings and Fixtures
5 a7_1_c1_4 NUMERIC A700001 00100 00400 Building Improvements 
6 a7_1_c1_5 NUMERIC A700001 00100 00500 Fixed Equipment       
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Here are the artefacts of this analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HCRIS record layout &lt;a href="//record-layout.csv"&gt;(CSV)&lt;/a&gt; and &lt;a href="//record-layout.xlsx"&gt;(XLSX)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="//hcris-record-layout-postgresql.sql"&gt;PostgreSQL table definition&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;
&lt;a href="//hcris-record-layout-mysql.sql"&gt;MySQL table definition&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Fathoming Email Headers</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Mon, 11 Oct 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/fathoming-email-headers-5dhp</link>
      <guid>https://dev.to/datawookie/fathoming-email-headers-5dhp</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hCwe8Sn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/featured.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hCwe8Sn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/featured.png" alt="Fathoming Email Headers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you pull back the curtain and take a look at what a &lt;em&gt;naked&lt;/em&gt; email looks like, then you might be astonished. For a start, despite the fact that an email can embed binary data, the message itself is generally transformed to 7 bit ASCII (the first 128 symbols in ASCII). This means that you can easily &lt;em&gt;read&lt;/em&gt; the contents (although not all of it might make much sense). At the top of the message are the headers. Your email client will normally extract some pertinent information from these headers (like the contents from the &lt;em&gt;From&lt;/em&gt;, &lt;em&gt;To&lt;/em&gt; and &lt;em&gt;Date&lt;/em&gt; fields) and display it at above the message body. But there’s a &lt;em&gt;lot&lt;/em&gt; more information embedded in the headers. In this post we’re going to take a look at what some of those headers mean and what information can be gleaned from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical Headers
&lt;/h2&gt;

&lt;p&gt;Below is the raw content of a simple (text) email sent from Alice (&lt;code&gt;alice@gmail.com&lt;/code&gt;) to Bob (&lt;code&gt;bob@yahoo.com&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Delivered-To: bob@yahoo.com
Received: from 10.214.167.142
 by atlas103.free.mail.gq1.yahoo.com with HTTPS; Fri, 8 Oct 2021 03:34:29 +0000
Return-Path: &amp;lt;alice@gmail.com&amp;gt;
X-Originating-Ip: [209.85.221.51]
Authentication-Results: atlas103.free.mail.gq1.yahoo.com;
 dkim=pass header.i=@gmail-com.20210112.gappssmtp.com header.s=20210112;
 spf=none smtp.mailfrom=gmail.com;
 dmarc=unknown header.from=gmail.com;
X-Apparently-To: bob@yahoo.com; Fri, 8 Oct 2021 03:34:29 +0000
Received: from 209.85.221.51 (EHLO mail-wr1-f51.google.com)
 by 10.214.167.142 with SMTPs
 (version=TLS1_2 cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
 Fri, 08 Oct 2021 03:34:29 +0000
Received: by mail-wr1-f51.google.com with SMTP id o20so25174853wro.3
        for &amp;lt;bob@yahoo.com&amp;gt;; Thu, 07 Oct 2021 20:34:29 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail-com.20210112.gappssmtp.com; s=20210112;
        h=message-id📅mime-version:to:from:subject:content-disposition
         :content-transfer-encoding:content-md5;
        bh=krdyOAo/jiepPlfm3uymwB2gf1qtzni7L7sg3hCmaSU=;
        b=8J0ymoL07NMNgk/0NXGCujWtAZ62KdnEk3HwxZpQS99M4PD4/MKKYhjrJxzt5QJGUq
         erS+1nXOeHZD5k7IVlUo7rDJZbDQdt4FFh1wOEaWc8CUPqBu3hJDSgDdWmQRVlsntnnc
         CB6tqF/VC3C4jdoBXX39npp+FFJSBNWcVsZLHdqj1dxhHWbIed3Q98Lfkh+rrb7xHBy4
         cKzdloNNisVPRKQXnNENWRxAF+22fS6DuvfsFyZLctlvgRg8WXGDQACt6WR4prfuV1R3
         thP7NoM7DIIYn3PnepZ3zAbN8P5GG+VOqv64L3sJNUkxrEcNczLdqDDwnyhfUtPKB0wP
         hrig==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20210112;
        h=x-gm-message-state:message-id:date:mime-version:to:from:subject
         :content-disposition:content-transfer-encoding:content-md5;
        bh=krdyOAo/jiepPlfm3uymwB2gf1qtzni7L7sg3hCmaSU=;
        b=ul2T8UXULfKNYI7WHtEG1+zImSks02kwY4jormij3ejAZpdUnYQeNuiBFWAZV+2SWU
         qfLLEEPLcU4Wp9CwX+CbdOZZVM2UKo00EDUjJ3eyOnv0MQy0aBajV27x6L5gKWcURuP4
         rPwGEOInoRE3scYbEwmimebQC8xNQD2kCVJH2HfEII3g5L5U8EVk4UH2HxT7LOwwYfx6
         Gwlhqf4z7xdJc56ywP7UjRXepIEKo2GRwUxs7BrM/v22380Yge6enehyfJCvRmQe+34z
         6Qh0eowKDRbKawIl9RHE6/h1OWBw9mC3I/VzHoXV7+DoXUUYJ883TuTSR47cQ7sqx3+Z
         24KQ==
Received: from allieyoo (host-92-12-241-137.as13285.net. [92.12.241.137])
        by smtp.gmail.com with ESMTPSA id u5sm1069389wrg.57.2021.10.07.20.34.28
        for &amp;lt;bob@yahoo.com&amp;gt;
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Thu, 07 Oct 2021 20:34:28 -0700 (PDT)
Message-ID: &amp;lt;615fbc44.1c69fb81.55d71.5951@mx.google.com&amp;gt;
Date: Thu, 07 Oct 2021 20:34:28 -0700 (PDT)
X-Google-Original-Date: Fri, 08 Oct 2021 03:34:27 GMT
From: alice@gmail.com
To: bob@yahoo.com
Subject: Hello
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Content-MD5: ZajifYh5KDgxtmS9i38K1A==
Content-Length: 14
Content-Language: en-GB

Hello, World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you skimmed through the raw message contents then (hopefully before your eyes glazed over) you would have noted that the headers consist of key-value pairs. For example, &lt;code&gt;Date: Thu, 07 Oct 2021 20:34:28 -0700 (PDT)&lt;/code&gt; has key &lt;code&gt;Date&lt;/code&gt; and value &lt;code&gt;Thu, 07 Oct 2021 20:34:28 -0700 (PDT)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You might also have noticed that a number of the header keys start with &lt;code&gt;X-&lt;/code&gt;. These are &lt;em&gt;custom headers&lt;/em&gt; inserted by the various processes that handle the mail while it’s being delivered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original Message Content
&lt;/h2&gt;

&lt;p&gt;For reference, here is the content of the message that was actually sent by Alice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Date: Fri, 08 Oct 2021 03:34:27 GMT
From: alice@gmail.com
To: bob@yahoo.com
Subject: Hello
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Content-MD5: ZajifYh5KDgxtmS9i38K1A==

Hello, World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message was generated and sent using the &lt;a href="https://github.com/datawookie/emayili"&gt;&lt;code&gt;{emayili}&lt;/code&gt;&lt;/a&gt; package. Clearly a lot of headers are introduced in the process of delivering the message!&lt;/p&gt;

&lt;h2&gt;
  
  
  Number of Headers
&lt;/h2&gt;

&lt;p&gt;You might have noticed that some of the header records in the example above occurred more than once. For example, the &lt;code&gt;Return-Path&lt;/code&gt; field. This is not uncommon. However, there are some rules about which headers may be repeated. These rules also indicates which headers are optional and which are mandatory. The table below (extracted from RFC 5322) indicates the limits on the number of times each type of header should appear in an email. The range of headers included in the table is only a subset of all possible headers. But it gives a fair idea of what the rules are. For example, the &lt;code&gt;From&lt;/code&gt; field must be specified, as must the &lt;code&gt;Date&lt;/code&gt; field. The &lt;code&gt;To&lt;/code&gt;, &lt;code&gt;Cc&lt;/code&gt; and &lt;code&gt;Bcc&lt;/code&gt; fields are all optional though. So, in principle, you can create a valid email with no recipients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H0lOY3y0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/email-header-limits.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H0lOY3y0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/email-header-limits.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Header Fields
&lt;/h2&gt;

&lt;p&gt;First we’ll take a look at the header fields which are part of the original (source) message as sent. These fields are all populated by the email client.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Date&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Date&lt;/code&gt; field specifies the date and time at which the message was sent.&lt;/p&gt;

&lt;p&gt;You might note that the date specified in the source message was &lt;code&gt;Fri, 08 Oct 2021 03:34:27 GMT&lt;/code&gt;, while the date in the delivered message was &lt;code&gt;Thu, 07 Oct 2021 20:34:28 -0700 (PDT)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The format of the date and time was originally defined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc822#section-5"&gt;RFC 822 Section 5&lt;/a&gt; and subsequently clarified in &lt;a href="https://datatracker.ietf.org/doc/html/rfc2822#section-3.3"&gt;RFC 2822 Section 3.3&lt;/a&gt;. However you will find that there is quite a variety in the formats used in practice.&lt;/p&gt;

&lt;p&gt;The custom &lt;code&gt;X-Google-Original-Date&lt;/code&gt; field retains the date and time in their original source format.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;To&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;To&lt;/code&gt; fields specify the email addresses of the sender and recipient of the message. The format for the addresses is detaied in &lt;a href="https://datatracker.ietf.org/doc/html/rfc822#section-6"&gt;RFC 822 Section 6&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to these fields you can also have &lt;code&gt;Cc&lt;/code&gt; (carbon copy.) and &lt;code&gt;Bcc&lt;/code&gt; (blind carbon copy.) fields, which specify additional recipients who are not the main intended recipient. The details of those listed under &lt;code&gt;Bcc&lt;/code&gt; are not visible to any of the recipients.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Subject&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Subject&lt;/code&gt; field doesn’t really require much explanation: it tells you what the message is about.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;MIME-Version&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/MIME"&gt;Multipurpose Internet Mail Extensions (MIME)&lt;/a&gt; is a format which allows email messages to contain content beyond mere text. MIME is described in great detail in &lt;a href="https://datatracker.ietf.org/doc/html/rfc1341"&gt;RFC 1341&lt;/a&gt;. The version of MIME is typically 1.0 because it appears that’s the only version available!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Content-Type&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Content-Type&lt;/code&gt; field specifies the &lt;a href="https://en.wikipedia.org/wiki/Media_type"&gt;media type&lt;/a&gt; (or MIME type) of data in the message. The type is specified as two-part identifier with &lt;code&gt;&amp;lt;type&amp;gt;/&amp;lt;subtype&amp;gt;&lt;/code&gt;. For example, &lt;code&gt;text/plain&lt;/code&gt; or &lt;code&gt;text/html&lt;/code&gt; for plain text or HTML content, and &lt;code&gt;image/png&lt;/code&gt; for a PNG image. An extensive list of a wide variety of media types can be found &lt;a href="https://www.iana.org/assignments/media-types/media-types.xhtml"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The media type is often followed by one or more parameters. For example, in Alice’s email the &lt;code&gt;Content-Type&lt;/code&gt; field is &lt;code&gt;text/plain; charset=utf-8; format=flowed&lt;/code&gt;, where the &lt;code&gt;charset&lt;/code&gt; and &lt;code&gt;format&lt;/code&gt; parameters indicate UTF-8 encoding of the text and flowed format (lines are not wrapped).&lt;/p&gt;

&lt;p&gt;In a multi-part message (for example, a message with both plain and HTML test as well as attachments) there will be multiple &lt;code&gt;Content-Type&lt;/code&gt; fields, one for each part of the message.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Content-Disposition&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Content-Disposition&lt;/code&gt; header indicates how content is to be displayed, and is generally either &lt;em&gt;inline&lt;/em&gt; or &lt;em&gt;attachment&lt;/em&gt;. &lt;a href="https://www.rfc-editor.org/rfc/rfc2183.html"&gt;RFC 2183&lt;/a&gt; is an extended discussion of this field. The range of permitted values is shown in the table below.&lt;/p&gt;

&lt;p&gt;html {&lt;br&gt;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', 'Fira Sans', 'Droid Sans', Arial, sans-serif;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_table {
&lt;/h1&gt;

&lt;p&gt;display: table;&lt;br&gt;
  border-collapse: collapse;&lt;br&gt;
  margin-left: 0;&lt;br&gt;
  margin-right: auto;&lt;br&gt;
  color: #333333;&lt;br&gt;
  font-size: 16px;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  font-style: normal;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  width: auto;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #A8A8A8;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #A8A8A8;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_heading {
&lt;/h1&gt;

&lt;p&gt;background-color: #FFFFFF;&lt;br&gt;
  text-align: center;&lt;br&gt;
  border-bottom-color: #FFFFFF;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_title {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  font-size: 125%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  padding-top: 4px;&lt;br&gt;
  padding-bottom: 4px;&lt;br&gt;
  border-bottom-color: #FFFFFF;&lt;br&gt;
  border-bottom-width: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_subtitle {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  font-size: 85%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  padding-top: 0;&lt;br&gt;
  padding-bottom: 6px;&lt;br&gt;
  border-top-color: #FFFFFF;&lt;br&gt;
  border-top-width: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_bottom_border {
&lt;/h1&gt;

&lt;p&gt;border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_col_headings {
&lt;/h1&gt;

&lt;p&gt;border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_col_heading {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: bottom;&lt;br&gt;
  padding-top: 5px;&lt;br&gt;
  padding-bottom: 6px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_column_spanner_outer {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 0;&lt;br&gt;
  padding-bottom: 0;&lt;br&gt;
  padding-left: 4px;&lt;br&gt;
  padding-right: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_column_spanner_outer:first-child {
&lt;/h1&gt;

&lt;p&gt;padding-left: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_column_spanner_outer:last-child {
&lt;/h1&gt;

&lt;p&gt;padding-right: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_column_spanner {
&lt;/h1&gt;

&lt;p&gt;border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  vertical-align: bottom;&lt;br&gt;
  padding-top: 5px;&lt;br&gt;
  padding-bottom: 5px;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
  display: inline-block;&lt;br&gt;
  width: 100%;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_group_heading {
&lt;/h1&gt;

&lt;p&gt;padding: 8px;&lt;br&gt;
  color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_empty_group_heading {
&lt;/h1&gt;

&lt;p&gt;padding: 0.5px;&lt;br&gt;
  color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_from_md &amp;gt; :first-child {
&lt;/h1&gt;

&lt;p&gt;margin-top: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_from_md &amp;gt; :last-child {
&lt;/h1&gt;

&lt;p&gt;margin-bottom: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  margin: 10px;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 1px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_stub {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-right-style: solid;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  padding-left: 12px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_summary_row {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_first_summary_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_grand_summary_row {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_first_grand_summary_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  border-top-style: double;&lt;br&gt;
  border-top-width: 6px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_striped {
&lt;/h1&gt;

&lt;p&gt;background-color: rgba(128, 128, 128, 0.05);&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_table_body {
&lt;/h1&gt;

&lt;p&gt;border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_footnotes {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  border-bottom-style: none;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_footnote {
&lt;/h1&gt;

&lt;p&gt;margin: 0px;&lt;br&gt;
  font-size: 90%;&lt;br&gt;
  padding: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_sourcenotes {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  border-bottom-style: none;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_sourcenote {
&lt;/h1&gt;

&lt;p&gt;font-size: 90%;&lt;br&gt;
  padding: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_left {
&lt;/h1&gt;

&lt;p&gt;text-align: left;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_center {
&lt;/h1&gt;

&lt;p&gt;text-align: center;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_right {
&lt;/h1&gt;

&lt;p&gt;text-align: right;&lt;br&gt;
  font-variant-numeric: tabular-nums;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_font_normal {
&lt;/h1&gt;

&lt;p&gt;font-weight: normal;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_font_bold {
&lt;/h1&gt;

&lt;p&gt;font-weight: bold;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_font_italic {
&lt;/h1&gt;

&lt;p&gt;font-style: italic;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_super {
&lt;/h1&gt;

&lt;p&gt;font-size: 65%;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  wkszanyzfl .gt_footnote_marks {
&lt;/h1&gt;

&lt;p&gt;font-style: italic;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  font-size: 65%;&lt;br&gt;
}&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content-Disposition Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;attachment&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;form-data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 7578 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;signal&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3204 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;alert&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3261 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;icon&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3261 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;render&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3261 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;recipient-list-history&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 5364 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;session&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3261 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;aib&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3893 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;early-session&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3959 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;recipient-list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 5363 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;notification&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 5438 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;by-reference&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 5621 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;info-package&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 6086 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;recording-session&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 7866 |&lt;/p&gt;

&lt;p&gt;There are also various parameters which can be specified for this field, with options extracted from the table below.&lt;/p&gt;

html {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', 'Fira Sans', 'Droid Sans', Arial, sans-serif;
}

#ejufukiovl .gt_table {
  display: table;
  border-collapse: collapse;
  margin-left: 0;
  margin-right: auto;
  color: #333333;
  font-size: 16px;
  font-weight: normal;
  font-style: normal;
  background-color: #FFFFFF;
  width: auto;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #A8A8A8;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #A8A8A8;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
}

#ejufukiovl .gt_heading {
  background-color: #FFFFFF;
  text-align: center;
  border-bottom-color: #FFFFFF;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#ejufukiovl .gt_title {
  color: #333333;
  font-size: 125%;
  font-weight: initial;
  padding-top: 4px;
  padding-bottom: 4px;
  border-bottom-color: #FFFFFF;
  border-bottom-width: 0;
}

#ejufukiovl .gt_subtitle {
  color: #333333;
  font-size: 85%;
  font-weight: initial;
  padding-top: 0;
  padding-bottom: 6px;
  border-top-color: #FFFFFF;
  border-top-width: 0;
}

#ejufukiovl .gt_bottom_border {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#ejufukiovl .gt_col_headings {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#ejufukiovl .gt_col_heading {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 6px;
  padding-left: 5px;
  padding-right: 5px;
  overflow-x: hidden;
}

#ejufukiovl .gt_column_spanner_outer {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 4px;
  padding-right: 4px;
}

#ejufukiovl .gt_column_spanner_outer:first-child {
  padding-left: 0;
}

#ejufukiovl .gt_column_spanner_outer:last-child {
  padding-right: 0;
}

#ejufukiovl .gt_column_spanner {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 5px;
  overflow-x: hidden;
  display: inline-block;
  width: 100%;
}

#ejufukiovl .gt_group_heading {
  padding: 8px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
}

#ejufukiovl .gt_empty_group_heading {
  padding: 0.5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: middle;
}

#ejufukiovl .gt_from_md &amp;gt; :first-child {
  margin-top: 0;
}

#ejufukiovl .gt_from_md &amp;gt; :last-child {
  margin-bottom: 0;
}

#ejufukiovl .gt_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  margin: 10px;
  border-top-style: solid;
  border-top-width: 1px;
  border-top-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  overflow-x: hidden;
}

#ejufukiovl .gt_stub {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 12px;
}

#ejufukiovl .gt_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#ejufukiovl .gt_first_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
}

#ejufukiovl .gt_grand_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#ejufukiovl .gt_first_grand_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-style: double;
  border-top-width: 6px;
  border-top-color: #D3D3D3;
}

#ejufukiovl .gt_striped {
  background-color: rgba(128, 128, 128, 0.05);
}

#ejufukiovl .gt_table_body {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#ejufukiovl .gt_footnotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#ejufukiovl .gt_footnote {
  margin: 0px;
  font-size: 90%;
  padding: 4px;
}

#ejufukiovl .gt_sourcenotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#ejufukiovl .gt_sourcenote {
  font-size: 90%;
  padding: 4px;
}

#ejufukiovl .gt_left {
  text-align: left;
}

#ejufukiovl .gt_center {
  text-align: center;
}

#ejufukiovl .gt_right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

#ejufukiovl .gt_font_normal {
  font-weight: normal;
}

#ejufukiovl .gt_font_bold {
  font-weight: bold;
}

#ejufukiovl .gt_font_italic {
  font-style: italic;
}

#ejufukiovl .gt_super {
  font-size: 65%;
}

#ejufukiovl .gt_footnote_marks {
  font-style: italic;
  font-weight: normal;
  font-size: 65%;
}


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content-Disposition Parameters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;filename&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;creation-date&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;modification-date&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;read-date&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;size&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2183 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 7578 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;voice&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 2421 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;handling&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 3204 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;preview-type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 7763 |&lt;br&gt;
| &lt;/p&gt;

&lt;p&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;| RFC 9078 |&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;Content-Transfer-Encoding&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Content-Transfer-Encoding&lt;/code&gt; field is document in &lt;a href="https://datatracker.ietf.org/doc/html/rfc2045#section-6"&gt;RFC 2045 Section 6&lt;/a&gt;. It specifies the way that the content has been encoded. Why is encoding necessary? Well, &lt;a href="https://datatracker.ietf.org/doc/html/rfc821"&gt;RFC 821&lt;/a&gt; which established SMTP specified that data would be represented by 7-bit ASCII characters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The TCP connection supports the transmission of 8-bit bytes. The SMTP data is 7-bit ASCII characters. Each character is transmitted as an 8-bit byte with the high-order bit cleared to zero. &lt;cite&gt;RFC 821&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Furthermore, the maximum length of a line was limited to 1000 characters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The maximum total length of a text line including the CRLF is 1000 characters (but not counting the leading dot duplicated for transparency). &lt;cite&gt;RFC 821&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The majority of data that you’d want to include in an email doesn’t comply with either of these requirements. So, in order for you to be able to include fancy Unicode characters in your HTML messages and attach cute cat pictures, those data need to be encoded in such a way that they can still be transmitted over SMTP.&lt;/p&gt;

&lt;p&gt;The most common values for this field are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;7bit&lt;/code&gt; (default) — Content is already in 7-bit ASCII (no encoding).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quoted-printable&lt;/code&gt; — &lt;a href="https://en.wikipedia.org/wiki/Quoted-printable"&gt;Quoted-Printable encoding&lt;/a&gt; uses the equal sign (&lt;code&gt;=&lt;/code&gt;) as an escape character to transform non-ASCII characters into something that can be represnted by ASCII. For example, the character é (&lt;a href="https://en.wikipedia.org/wiki/%C3%89"&gt;e-acute&lt;/a&gt;), which is represented in UTF-8 encoding by the bytes &lt;code&gt;0xC3&lt;/code&gt; &lt;code&gt;0xA9&lt;/code&gt;, is encoded as &lt;code&gt;=C3=A9&lt;/code&gt;. This encoding also limits line length to 76 characters, where lines are terminated by a soft line break represented by &lt;code&gt;=&lt;/code&gt;. Quoted-Printable encoding is most often used to encode text data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base64&lt;/code&gt; — &lt;a href="https://en.wikipedia.org/wiki/Base64"&gt;Base64 encoding&lt;/a&gt; encodes data into 6-bit digits which are packed in groups of four, so that 4 Base64 digits represent 24 bits or 3 bytes. Base64 is most often used to encode binary data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To illustrate the difference between these various coding mechanisms, suppose that you wanted to send the following message in an email:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J’interdis aux marchands de vanter trop leurs marchandises. Car ils se font vite pédagogues et t’enseignent comme but ce qui n’est par essence qu’un moyen, et te trompant ainsi sur la route à suivre les voilà bientôt qui te dégradent, car si leur musique est vulgaire ils te fabriquent pour te la vendre une âme vulgaire.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using Quoted-Printable encoding the email would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Type: text/plain; charset=utf-8; format=flowed
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-MD5: XjpBVdSoL+frxc/IjptLkA==

J'interdis aux marchands de vanter trop leurs marchandises. Car ils se font=
 vite p=C3=A9dagogues et t'enseignent comme but ce qui n'est par essence qu=
'un moyen, et te trompant ainsi sur la route =C3=A0 suivre les voil=C3=A0 b=
ient=C3=B4t qui te d=C3=A9gradent, car si leur musique est vulgaire ils te =
fabriquent pour te la vendre une =C3=A2me vulgaire.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All non-ASCII characters have been encoded and the text has been wrapped to a width of only 76 characters. Most of the content is still legible though. By contrast, Base64 encoding would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Type: text/plain; charset=utf-8; format=flowed
Content-Disposition: inline
Content-Transfer-Encoding: base64
Content-MD5: XjpBVdSoL+frxc/IjptLkA==

SidpbnRlcmRpcyBhdXggbWFyY2hhbmRzIGRlIHZhbnRlciB0cm9wIGxldXJzIG1hcmNoYW5kaXNl
cy4gQ2FyIGlscyBzZSBmb250IHZpdGUgcMOpZGFnb2d1ZXMgZXQgdCdlbnNlaWduZW50IGNvbW1l
IGJ1dCBjZSBxdWkgbidlc3QgcGFyIGVzc2VuY2UgcXUndW4gbW95ZW4sIGV0IHRlIHRyb21wYW50
IGFpbnNpIHN1ciBsYSByb3V0ZSDDoCBzdWl2cmUgbGVzIHZvaWzDoCBiaWVudMO0dCBxdWkgdGUg
ZMOpZ3JhZGVudCwgY2FyIHNpIGxldXIgbXVzaXF1ZSBlc3QgdnVsZ2FpcmUgaWxzIHRlIGZhYnJp
cXVlbnQgcG91ciB0ZSBsYSB2ZW5kcmUgdW5lIMOibWUgdnVsZ2FpcmUu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again it’s all ASCII characters with a line width of only 76 characters. But now the content is completely illegible (unless you can Base64 decode in your head!).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Content-MD5&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/MD5"&gt;MD5&lt;/a&gt; is a hash function that produces 128-bit hash values. For the purposes of email it’s used as a checksum to verify data integrity. The &lt;code&gt;Content-MD5&lt;/code&gt; header field is a Base64 encoded representation of the MD5 has of the contents of the message component.&lt;/p&gt;

&lt;p&gt;In the two examples above (for Quoted-Printable and Base64 content encoding) you’ll notice that the value of the &lt;code&gt;Content-MD5&lt;/code&gt; field, XjpBVdSoL+frxc/IjptLkA==, is the same. This is because the contents of the message components is the same despite them being encoded in different ways. Decoding the Base64 yields the raw MD5 hash, &lt;code&gt;r&lt;/code&gt;msg_unicode_md5_hex`. Check this against the results from the &lt;a href="https://www.md5hashgenerator.com/"&gt;MD5 Hash Generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wi4gbY0K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/md5hashgenerator.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wi4gbY0K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/md5hashgenerator.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Transit Header Fields
&lt;/h2&gt;

&lt;p&gt;Now let’s look at the header fields introduced during delivery.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Message-ID&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Message-ID&lt;/code&gt; field uniquely identifies a specific message.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Message-ID: &amp;lt;615fbc44.1c69fb81.55d71.5951@mx.google.com&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The format of the identifier is similar to that of an email address, consisting of two components: an unique identifier (for example, &lt;code&gt;615fbc44.1c69fb81.55d71.5951&lt;/code&gt;) and the domain name of the mail server (for example, &lt;code&gt;mx.google.com&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Message-ID&lt;/code&gt; is important for linking messages together into &lt;em&gt;threads&lt;/em&gt;, where the &lt;code&gt;In-Reply-To&lt;/code&gt; and &lt;code&gt;References&lt;/code&gt; headers are used to reference earlier messages via their &lt;code&gt;Message-ID&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Received&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;There are multiple &lt;code&gt;Received&lt;/code&gt; entries in the header. These trace the route that the message took from Alice to Bob.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Received: from 10.214.167.142&lt;br&gt;
 by atlas103.free.mail.gq1.yahoo.com with HTTPS; Fri, 8 Oct 2021 03:34:29 +0000&lt;br&gt;
Received: from 209.85.221.51 (EHLO mail-wr1-f51.google.com)&lt;br&gt;
 by 10.214.167.142 with SMTPs&lt;br&gt;
 (version=TLS1_2 cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);&lt;br&gt;
 Fri, 08 Oct 2021 03:34:29 +0000&lt;br&gt;
Received: by mail-wr1-f51.google.com with SMTP id o20so25174853wro.3&lt;br&gt;
        for &amp;lt;bob@yahoo.com&amp;gt;; Thu, 07 Oct 2021 20:34:29 -0700 (PDT)&lt;br&gt;
Received: from allieyoo (host-92-12-241-137.as13285.net. [92.12.241.137])&lt;br&gt;
        by smtp.gmail.com with ESMTPSA id u5sm1069389wrg.57.2021.10.07.20.34.28&lt;br&gt;
        for &amp;lt;bob@yahoo.com&amp;gt;&lt;br&gt;
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);&lt;br&gt;
        Thu, 07 Oct 2021 20:34:28 -0700 (PDT)&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The message was sent initially to the Gmail SMTP server &lt;code&gt;smtp.gmail.com&lt;/code&gt; (IP 64.233.184.109). This is the server configured by Alice for her outgoing email. Where is that server? Good question. I used &lt;a href="https://ipgeolocation.io/"&gt;ipgeolocation&lt;/a&gt; to find out (slightly abridged output from their API below).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
{&lt;br&gt;
  "ip": "64.233.184.109",&lt;br&gt;
  "continent_name": "North America",&lt;br&gt;
  "country_name": "United States",&lt;br&gt;
  "state_prov": "California",&lt;br&gt;
  "district": "Old Mountain View",&lt;br&gt;
  "city": "Mountain View",&lt;br&gt;
  "zipcode": "94041-1238",&lt;br&gt;
  "latitude": "37.39500",&lt;br&gt;
  "longitude": "-122.08167",&lt;br&gt;
  "isp": "Google LLC",&lt;br&gt;
  "organization": "Google LLC",&lt;br&gt;
  "time_zone": {&lt;br&gt;
    "name": "America/Los_Angeles",&lt;br&gt;
    "offset": -8,&lt;br&gt;
    "is_dst": true,&lt;br&gt;
    "dst_savings": 1&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So Alice’s message went from her computer in the UK to a server in Mountain View, California on the west coast of the USA. That’s quite a big leap! We can use the other &lt;code&gt;Received&lt;/code&gt; fields to track its progress from there. Next stop was &lt;code&gt;mail-wr1-f51.google.com&lt;/code&gt; (IP 209.85.221.51), also located in Mountain View, California. From there it went to a machine on a private network (IP 10.214.167.142) before finally being delivered to &lt;code&gt;atlas103.free.mail.gq1.yahoo.com&lt;/code&gt;, presumably another server located on a private network.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Received&lt;/code&gt; field can also include information on the encryption (see references to &lt;code&gt;TLS&lt;/code&gt;) and the protocol (see &lt;code&gt;ESMTPSA&lt;/code&gt;, &lt;code&gt;SMTP&lt;/code&gt; and &lt;code&gt;SMTPs&lt;/code&gt;) being used.&lt;/p&gt;

&lt;p&gt;html {&lt;br&gt;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', 'Fira Sans', 'Droid Sans', Arial, sans-serif;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_table {
&lt;/h1&gt;

&lt;p&gt;display: table;&lt;br&gt;
  border-collapse: collapse;&lt;br&gt;
  margin-left: 0;&lt;br&gt;
  margin-right: auto;&lt;br&gt;
  color: #333333;&lt;br&gt;
  font-size: 16px;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  font-style: normal;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  width: auto;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #A8A8A8;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #A8A8A8;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_heading {
&lt;/h1&gt;

&lt;p&gt;background-color: #FFFFFF;&lt;br&gt;
  text-align: center;&lt;br&gt;
  border-bottom-color: #FFFFFF;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_title {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  font-size: 125%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  padding-top: 4px;&lt;br&gt;
  padding-bottom: 4px;&lt;br&gt;
  border-bottom-color: #FFFFFF;&lt;br&gt;
  border-bottom-width: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_subtitle {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  font-size: 85%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  padding-top: 0;&lt;br&gt;
  padding-bottom: 6px;&lt;br&gt;
  border-top-color: #FFFFFF;&lt;br&gt;
  border-top-width: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_bottom_border {
&lt;/h1&gt;

&lt;p&gt;border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_col_headings {
&lt;/h1&gt;

&lt;p&gt;border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_col_heading {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: bottom;&lt;br&gt;
  padding-top: 5px;&lt;br&gt;
  padding-bottom: 6px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_column_spanner_outer {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 0;&lt;br&gt;
  padding-bottom: 0;&lt;br&gt;
  padding-left: 4px;&lt;br&gt;
  padding-right: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_column_spanner_outer:first-child {
&lt;/h1&gt;

&lt;p&gt;padding-left: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_column_spanner_outer:last-child {
&lt;/h1&gt;

&lt;p&gt;padding-right: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_column_spanner {
&lt;/h1&gt;

&lt;p&gt;border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  vertical-align: bottom;&lt;br&gt;
  padding-top: 5px;&lt;br&gt;
  padding-bottom: 5px;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
  display: inline-block;&lt;br&gt;
  width: 100%;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_group_heading {
&lt;/h1&gt;

&lt;p&gt;padding: 8px;&lt;br&gt;
  color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_empty_group_heading {
&lt;/h1&gt;

&lt;p&gt;padding: 0.5px;&lt;br&gt;
  color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_from_md &amp;gt; :first-child {
&lt;/h1&gt;

&lt;p&gt;margin-top: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_from_md &amp;gt; :last-child {
&lt;/h1&gt;

&lt;p&gt;margin-bottom: 0;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  margin: 10px;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 1px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 1px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 1px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  vertical-align: middle;&lt;br&gt;
  overflow-x: hidden;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_stub {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  font-size: 100%;&lt;br&gt;
  font-weight: initial;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  border-right-style: solid;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
  padding-left: 12px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_summary_row {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_first_summary_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_grand_summary_row {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  text-transform: inherit;&lt;br&gt;
  padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_first_grand_summary_row {
&lt;/h1&gt;

&lt;p&gt;padding-top: 8px;&lt;br&gt;
  padding-bottom: 8px;&lt;br&gt;
  padding-left: 5px;&lt;br&gt;
  padding-right: 5px;&lt;br&gt;
  border-top-style: double;&lt;br&gt;
  border-top-width: 6px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_striped {
&lt;/h1&gt;

&lt;p&gt;background-color: rgba(128, 128, 128, 0.05);&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_table_body {
&lt;/h1&gt;

&lt;p&gt;border-top-style: solid;&lt;br&gt;
  border-top-width: 2px;&lt;br&gt;
  border-top-color: #D3D3D3;&lt;br&gt;
  border-bottom-style: solid;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_footnotes {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  border-bottom-style: none;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_footnote {
&lt;/h1&gt;

&lt;p&gt;margin: 0px;&lt;br&gt;
  font-size: 90%;&lt;br&gt;
  padding: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_sourcenotes {
&lt;/h1&gt;

&lt;p&gt;color: #333333;&lt;br&gt;
  background-color: #FFFFFF;&lt;br&gt;
  border-bottom-style: none;&lt;br&gt;
  border-bottom-width: 2px;&lt;br&gt;
  border-bottom-color: #D3D3D3;&lt;br&gt;
  border-left-style: none;&lt;br&gt;
  border-left-width: 2px;&lt;br&gt;
  border-left-color: #D3D3D3;&lt;br&gt;
  border-right-style: none;&lt;br&gt;
  border-right-width: 2px;&lt;br&gt;
  border-right-color: #D3D3D3;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_sourcenote {
&lt;/h1&gt;

&lt;p&gt;font-size: 90%;&lt;br&gt;
  padding: 4px;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_left {
&lt;/h1&gt;

&lt;p&gt;text-align: left;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_center {
&lt;/h1&gt;

&lt;p&gt;text-align: center;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_right {
&lt;/h1&gt;

&lt;p&gt;text-align: right;&lt;br&gt;
  font-variant-numeric: tabular-nums;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_font_normal {
&lt;/h1&gt;

&lt;p&gt;font-weight: normal;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_font_bold {
&lt;/h1&gt;

&lt;p&gt;font-weight: bold;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_font_italic {
&lt;/h1&gt;

&lt;p&gt;font-style: italic;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_super {
&lt;/h1&gt;

&lt;p&gt;font-size: 65%;&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  qiolitmyfl .gt_footnote_marks {
&lt;/h1&gt;

&lt;p&gt;font-style: italic;&lt;br&gt;
  font-weight: normal;&lt;br&gt;
  font-size: 65%;&lt;br&gt;
}&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mail Protocol Types&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESMTPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESMTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESMTPSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LMTPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LMTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LMTPSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8SMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8SMTPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8SMTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8SMTPSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8LMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8LMTPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8LMTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8LMTPSA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There can also be an &lt;code&gt;X-Received&lt;/code&gt; field which is a custom field containing information similar to the &lt;code&gt;Received&lt;/code&gt; field.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;X-Originating-Ip&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/X-Originating-IP"&gt;&lt;code&gt;X-Originating-Ip&lt;/code&gt; field&lt;/a&gt; identifies the IP address of the sender. This is relevant, for example, when you send an email using a web email client. In this case the web client communicates with the SMTP server, but the content of this field will be your actual IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Delivered-To&lt;/code&gt; and &lt;code&gt;Return-Path&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Delivered-To&lt;/code&gt; field records the email address to which the message was actually delivered. Somewhat surprisingly, this can be different to the addresses specified in the &lt;code&gt;To&lt;/code&gt;, &lt;code&gt;Cc&lt;/code&gt; or &lt;code&gt;Bcc&lt;/code&gt; fields. How does this happen? An email message generally passes through numerous servers and processes between the sender and the recipient. At various stages in this process rules might be applied which modify the delivery of the message. Suppose, for example, that Bob (&lt;code&gt;bob@yahoo.com&lt;/code&gt;) is one of many clients with who Alice communicates. If she sends out a general email to all of her clients then she will probably use a &lt;em&gt;mailing list&lt;/em&gt; rather than adding the address of each client individually. The message &lt;code&gt;To&lt;/code&gt; field would then contain the name of the mailing list, but this would be expanded en route to a list of individual email addresses, and these would appear in the &lt;code&gt;Delivered-To&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Sometimes an email doesn’t get delivered. Maybe there’s a problem with the recipients email server. Or perhaps the address of the recipient has changed. In this case the email will &lt;em&gt;bounce&lt;/em&gt; and a &lt;em&gt;bounce message&lt;/em&gt; will be returned which informs the sender that the email did not reach its destination. The &lt;code&gt;Return-Path&lt;/code&gt; field specifies the address to which the bounce message should be delivered. If, for example, you’re sending email to a large mailing list and some of the addresses on that list are unreliable then it can be useful to specify a different &lt;code&gt;Return-Path&lt;/code&gt; address so that bounce messages don’t end up cluttering your inbox. Although you can specify a value for &lt;code&gt;Return-Path&lt;/code&gt; it’s possible that your email server will override this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q1dQipvn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/address-not-found.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q1dQipvn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/fathoming-email-headers/address-not-found.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Authentication-Results&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Authentication-Results&lt;/code&gt; header contains information on what authentication methods have been applied to a message. It will often include information on the following protocols, all of which are intended to detected forged sender addresses (also known as “email spoofing”):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"&gt;DKIM&lt;/a&gt; (DomainKeys Identified Mail)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework"&gt;SPF&lt;/a&gt; (Sender Policy Framework) and&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/DMARC"&gt;DMARC&lt;/a&gt; (Domain-based Message Authentication, Reporting and Conformance).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Authentication-Results: atlas103.free.mail.gq1.yahoo.com;&lt;br&gt;
 dkim=pass header.i=@gmail-com.20210112.gappssmtp.com header.s=20210112;&lt;br&gt;
 spf=none smtp.mailfrom=gmail.com;&lt;br&gt;
 dmarc=unknown header.from=gmail.com;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the example above the results for each of these methods are listed. Valid results for each method are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DKIM — &lt;code&gt;pass&lt;/code&gt;, &lt;code&gt;fail&lt;/code&gt; or &lt;code&gt;none&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SPF — &lt;code&gt;pass&lt;/code&gt;, &lt;code&gt;fail&lt;/code&gt;, &lt;code&gt;softfail&lt;/code&gt;, &lt;code&gt;neutral&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;temperror&lt;/code&gt; or &lt;code&gt;permerror&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DMARC — &lt;code&gt;pass&lt;/code&gt;, &lt;code&gt;fail&lt;/code&gt;, &lt;code&gt;bestguesspass&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt; or &lt;code&gt;unknown&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;DKIM-Signature&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;DKIM (DomainKeys Identified Mail) is an authentication protocol. A DKIM Signature verifies the DNS domain of the email sender. The DNS entry for a mail server is associated with a public key, that is published and freely available. The DKIM Signature is a signed with the corresponding private key. Using the public key it’s then possible to establish the validity of the sender’s email address.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;&lt;br&gt;
        d=gmail-com.20210112.gappssmtp.com; s=20210112;&lt;br&gt;
        h=message-id📅mime-version:to:from:subject:content-disposition&lt;br&gt;
         :content-transfer-encoding:content-md5;&lt;br&gt;
        bh=krdyOAo/jiepPlfm3uymwB2gf1qtzni7L7sg3hCmaSU=;&lt;br&gt;
        b=8J0ymoL07NMNgk/0NXGCujWtAZ62KdnEk3HwxZpQS99M4PD4/MKKYhjrJxzt5QJGUq&lt;br&gt;
         erS+1nXOeHZD5k7IVlUo7rDJZbDQdt4FFh1wOEaWc8CUPqBu3hJDSgDdWmQRVlsntnnc&lt;br&gt;
         CB6tqF/VC3C4jdoBXX39npp+FFJSBNWcVsZLHdqj1dxhHWbIed3Q98Lfkh+rrb7xHBy4&lt;br&gt;
         cKzdloNNisVPRKQXnNENWRxAF+22fS6DuvfsFyZLctlvgRg8WXGDQACt6WR4prfuV1R3&lt;br&gt;
         thP7NoM7DIIYn3PnepZ3zAbN8P5GG+VOqv64L3sJNUkxrEcNczLdqDDwnyhfUtPKB0wP&lt;br&gt;
         hrig==&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The DKIM Signature field is comprised of a number of elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;v&lt;/code&gt; — the DKIM version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt; — the signing algorithm&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c&lt;/code&gt; — algorithm used to canonicalise the header and body (optional)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;d&lt;/code&gt; — Signing Domain Identifier (SDID) is the domain used to sign the email&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s&lt;/code&gt; — a selector&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;h&lt;/code&gt; — list of header fields that were signed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bh&lt;/code&gt; — a hash of the message body&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b&lt;/code&gt; — signature of the header fields and body.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The custom &lt;code&gt;X-Google-DKIM-Signature&lt;/code&gt; field is another DKIM signature added by Gmail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There’s obviously a lot of complexity lurking in those email headers. I’ve really learned a lot while researching this post and I hope that this information will be useful to you too.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Medusa: A Multi-Headed Tor Proxy</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Mon, 04 Oct 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/medusa-a-multi-headed-tor-proxy-2ng0</link>
      <guid>https://dev.to/datawookie/medusa-a-multi-headed-tor-proxy-2ng0</guid>
      <description>&lt;p&gt;At &lt;a href="https://fathomdata.dev/"&gt;Fathom Data&lt;/a&gt; we have a few projects which require us to send HTTP requests from an evolving selection of IP addresses. This post details one approach which uses &lt;a href="https://www.torproject.org/"&gt;Tor (The Onion Router)&lt;/a&gt; as a proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ND7V7foN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ND7V7foN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/featured.jpg" alt="Medusa: A Multi-Headed Tor Proxy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Proxy Server?
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Proxy_server"&gt;proxy server&lt;/a&gt; acts as an intermediary between a client and a server. When a request goes through a proxy server there is no direct connection between the client and the server. The client connects to the proxy and the proxy then connects to the server. Requests and responses pass through the proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PM0wOQO_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/osi-model.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PM0wOQO_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/osi-model.svg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Thanks to &lt;span&gt;@axiematic&lt;/span&gt; for the figure.&lt;br&gt;
&lt;/p&gt;
&lt;h2&gt;
  
  
  HTTP &amp;amp; SOCKS Proxies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;HTTP&lt;/a&gt; (HyperText Transfer Protocol) is the dominant protocol for information exchange on the internet. HTTP is connectionless. This means that the client (often a browser) sends a request to a server. The server then replies with a response. Once this interaction is over there is no persistent connection between the client and the server. Any further interactions require new connections.&lt;/p&gt;

&lt;p&gt;An HTTP proxy uses the HTTP protocol for all interactions with the client and server. As a result, an HTTP proxy is only able to handle HTTP and HTTPS requests. An HTTP proxy is also able to filter or modify the content of the requests and responses passing through it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/SOCKS"&gt;SOCKS&lt;/a&gt; is another internet protocol. Whereas as HTTP is an application layer protocol (at the top of the &lt;a href="https://en.wikipedia.org/wiki/OSI_model"&gt;OSI Model&lt;/a&gt;), SOCKS is a lower level protocol in the session layer.&lt;/p&gt;

&lt;p&gt;A SOCKS proxy uses the SOCKS protocol. Since it’s secure (the name is an abbreviation for “SOCKet Secure”), a SOCKS proxy cannot understand the contents of requests or responses, so is unable to modify or filter them. Since it operates at a lower level in the networking hierarchy, a SOCKS proxy is also faster and more flexible than an HTTP proxy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tor Proxy Docker Image
&lt;/h2&gt;

&lt;p&gt;We constructed a Docker image which uses the Tor network to expose both SOCKS and HTTP proxies. The image uses the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tor&lt;/li&gt;
&lt;li&gt;HAProxy and&lt;/li&gt;
&lt;li&gt;Privoxy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relationship between these components is detailed in the figure below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zhAURUO_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/rotating-tor-proxy.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zhAURUO_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/rotating-tor-proxy.svg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
Components in the Medusa Proxy (single headed configuration).&lt;br&gt;
&lt;/p&gt;
&lt;h3&gt;
  
  
  Tor
&lt;/h3&gt;

&lt;p&gt;Tor provides an anonymous SOCKS proxy. The image will run multiple Tor instances, each of which will (in general) have a different exit node. This means that requests being routed through each instance will appear to come from a distinct IP address.&lt;/p&gt;
&lt;h3&gt;
  
  
  HAProxy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.haproxy.com/"&gt;HAProxy&lt;/a&gt; is a high availability proxy server and load balancer (spreads requests across multiple services). HAProxy is used to distribute SOCKS requests across the Tor instances using a &lt;a href="https://en.wikipedia.org/wiki/Round-robin_scheduling"&gt;round robin scheduling&lt;/a&gt; strategy.&lt;/p&gt;
&lt;h3&gt;
  
  
  Privoxy
&lt;/h3&gt;

&lt;p&gt;To provide for services that prefer to communicate via HTTP or cannot communicate via SOCKS, &lt;a href="https://www.privoxy.org/"&gt;Privoxy&lt;/a&gt; is used to accept HTTP requests and forward them as SOCKS requests to HAProxy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running the Docker Image
&lt;/h2&gt;

&lt;p&gt;Let’s spin up a container and take a look.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    docker run \
        -p 8800:8800 \
        -p 8888:8888 \
        -p 1080:1080 \
        -p 2090:2090 \
        datawookie/medusa-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re mapped a lot of ports.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8800 — list of proxy URLs (as &lt;code&gt;text/plain&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;8888 — Privoxy port (HTTP protocol)&lt;/li&gt;
&lt;li&gt;1080 — HAProxy port (SOCKS protocol) and&lt;/li&gt;
&lt;li&gt;2090 — HAProxy statistics port.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not all of them are required, but they all fulfill a distinct purpose. Below are some alternative ways to invoke the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# HTTP proxy on port 8888
docker run -p 8888:8888 datawookie/medusa-proxy
# SOCKS proxy on port 1080
docker run -p 1080:1080 datawookie/medusa-proxy
# Both HTTP and SOCKS proxies
docker run -p 8888:8888 -p 1080:1080 datawookie/medusa-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we’ve got a running container we can set up a client to use the proxy. We’ll start with a browser and then look at &lt;code&gt;curl&lt;/code&gt; on the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser
&lt;/h2&gt;

&lt;p&gt;Set up your browser to use the HTTP proxy. You could equally choose to use the SOCKS proxy, which will have an IP address of 127.0.0.1 and port 1080.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Djv_eUL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/browser-configuration.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Djv_eUL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/browser-configuration.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve configured the proxy settings in your browser, head over to &lt;a href="https://whatismyipaddress.com/"&gt;What Is My IP Address&lt;/a&gt; to check on your effective IP address. Refresh the page to confirm that the IP address changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Further testing is easier to do (and record) on the command line. To illustrate how the proxy works we’ll send requests to &lt;a href="http://httpbin.org/ip"&gt;http://httpbin.org/ip&lt;/a&gt; to retrieve our effective IP address.&lt;/p&gt;

&lt;p&gt;First let’s look at the set of IP addresses reported by the container. Below is an extract from the Docker logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2021-09-29 04:24:40 [INFO] Testing proxy (port 10000): 195.154.35.52.
2021-09-29 04:24:40 [INFO] Testing proxy (port 10001): 178.20.55.18.
2021-09-29 04:24:40 [INFO] Testing proxy (port 10002): 195.176.3.24.
2021-09-29 04:24:40 [INFO] Testing proxy (port 10003): 185.220.100.252.
2021-09-29 04:24:40 [INFO] Testing proxy (port 10004): 185.220.101.198.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t worry about the port numbers (those are only relevant within the container). Note that there are five proxies (one for each Tor instance) and that each has a different IP address.&lt;/p&gt;

&lt;p&gt;Now send out a series of requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://httpbin.org/ip

{
  "origin": "195.154.35.52"
}

curl http://httpbin.org/ip

{
  "origin": "178.20.55.18"
}

curl http://httpbin.org/ip

{
  "origin": "195.176.3.24"
}

curl http://httpbin.org/ip

{
  "origin": "185.220.100.252"
}

curl http://httpbin.org/ip

{
  "origin": "185.220.101.198"
}

curl http://httpbin.org/ip

{
  "origin": "195.154.35.52"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that each request appears to originate from a distinct IP address and that once we’ve cycled through all of the Tor instances, we wrap back to the first one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rotating
&lt;/h3&gt;

&lt;p&gt;The exit nodes are periodically rotated. Some time later we see that we’re using a different set of IP addresses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2021-09-29 04:39:13 [INFO] Testing proxy (port 10000): 171.25.193.20.
2021-09-29 04:39:13 [INFO] Testing proxy (port 10001): 199.249.230.87.
2021-09-29 04:39:14 [INFO] Testing proxy (port 10002): 185.220.101.132.
2021-09-29 04:39:14 [INFO] Testing proxy (port 10003): 199.249.230.184.
2021-09-29 04:39:15 [INFO] Testing proxy (port 10004): 23.129.64.161.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Weakness
&lt;/h3&gt;

&lt;p&gt;So this is great, but there’s one major weakness: if any one of the Tor instances became unavailable then the proxy would be marked as broken (regardless of whether the other Tor instances were fine or not). To get around this I have been running multiple containers, each assigned to a different port. With this setup, even if a few of the containers are marked as broken, there are still others which are considered healthy and able to accept requests. Although easy enough to automate, the logistics associated with this setup are a little onerous.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be convenient if there was just a single container which exposes multiple proxies, each of which is hooked up to a distinct set of Tor instances?&lt;/p&gt;

&lt;h2&gt;
  
  
  Beast with Many Heads
&lt;/h2&gt;

&lt;p&gt;So, rather than exposing just a single unit per container, the Medusa proxy can cater for multiple proxy units or &lt;em&gt;heads&lt;/em&gt;. What this means is that there can be multiple copies of the components illustrated in the diagram above. All heads are served from the same network location but on different ports.&lt;/p&gt;

&lt;p&gt;There are a few environment variables which can be used to tweak the configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TORS&lt;/code&gt; — Number of heads (default: 2)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TORS&lt;/code&gt; — Number of Tor instances (default: 5)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HAPROXY_LOGIN&lt;/code&gt; — Username for HAProxy (default: “admin”)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HAPROXY_PASSWORD&lt;/code&gt; — Password for HAProxy (default: “admin”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s give this a try. We’ll launch Medusa with 4 heads (each linking to 3 Tor instances) and only map the ports for the HTTP proxies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    docker run \
        -e TORS=3 \
        -e HEADS=4 \
        -p 8800:8800 \
        -p 8888:8888 -p 8889:8889 -p 8890:8890 -p 8891:8891 \
        datawookie/medusa-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we have four proxy connections at ports 8888, 8889, 8890 and 8891. If we look at the Docker logs then we see that there are 3 Tor endpoints for each head.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2021-09-30 08:05:34,606 [INFO] Testing proxies.
2021-09-30 08:05:34,606 [INFO] * Privoxy 0
2021-09-30 08:05:35,295 [INFO] Testing proxy (port 10000): 185.220.101.2.
2021-09-30 08:05:35,753 [INFO] Testing proxy (port 10001): 198.98.62.74.
2021-09-30 08:05:36,041 [INFO] Testing proxy (port 10002): 185.220.100.245.
2021-09-30 08:05:36,041 [INFO] * Privoxy 1
2021-09-30 08:05:36,652 [INFO] Testing proxy (port 10003): 37.123.163.58.
2021-09-30 08:05:37,304 [INFO] Testing proxy (port 10004): 37.187.196.70.
2021-09-30 08:05:37,634 [INFO] Testing proxy (port 10005): 77.68.20.217.
2021-09-30 08:05:37,634 [INFO] * Privoxy 2
2021-09-30 08:05:37,909 [INFO] Testing proxy (port 10006): 89.163.143.8.
2021-09-30 08:05:38,954 [INFO] Testing proxy (port 10007): 185.220.101.10.
2021-09-30 08:05:39,721 [INFO] Testing proxy (port 10008): 195.206.105.217.
2021-09-30 08:05:39,721 [INFO] * Privoxy 3
2021-09-30 08:05:40,050 [INFO] Testing proxy (port 10009): 185.220.100.243.
2021-09-30 08:05:41,010 [INFO] Testing proxy (port 10010): 185.220.101.43.
2021-09-30 08:05:41,415 [INFO] Testing proxy (port 10011): 185.185.170.27.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Proxy List
&lt;/h2&gt;

&lt;p&gt;A proxy list is served as a plain text file on port 8800. This can be used to configure rotating proxies in clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H_YHN-CC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/proxy-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H_YHN-CC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/proxy-list.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Statistics
&lt;/h2&gt;

&lt;p&gt;You can monitor the performance of the proxies via the statistics interface, which each HAProxy instance exposes via a port numbered sequentially from 2090.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dhtlR8LZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/proxy-statistics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dhtlR8LZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/10/medusa-multi-headed-tor-proxy/fig/proxy-statistics.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Medusa with Scrapy
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Technical Details
&lt;/h2&gt;

&lt;p&gt;The image is derived from the official &lt;a href="https://hub.docker.com/_/alpine"&gt;Alpine image&lt;/a&gt;, onto which Python 3, Tor, HAProxy and Privoxy are installed. The configuration files for each of the services are created from templates using &lt;a href="https://en.wikipedia.org/wiki/Jinja_(template_engine)"&gt;Jinja templates&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{emayili} Right-to-Left</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Fri, 24 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-right-to-left-1ppn</link>
      <guid>https://dev.to/datawookie/emayili-right-to-left-1ppn</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vNGcaXjP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-right-to-left/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vNGcaXjP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-right-to-left/featured.jpg" alt="{emayili} Right-to-Left"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yoav Raskin &lt;a href="https://github.com/datawookie/emayili/issues/81"&gt;suggested&lt;/a&gt; that it would be useful to support right-to-left (RTL) documents in &lt;code&gt;{emayili}&lt;/code&gt;, so that languages like Hebrew, Arabic and Aramaic would render properly. I’ll be honest, this was not something that I had previously considered. But agreed, it &lt;em&gt;would&lt;/em&gt; be a cool feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)

packageVersion("emayili")

[1] '0.5.5'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  RTL in CSS
&lt;/h2&gt;

&lt;p&gt;The first step to make this happen was writing a short CSS file, &lt;code&gt;rtl.css&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.rtl {
  direction: rtl;
  color: green;
}

body {
  margin: 20px 0px;
}

figure {
  margin-bottom: 20px;
}

.rtl figcaption {
  direction: inherit;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes use of the CSS &lt;code&gt;direction&lt;/code&gt; property (which by default is &lt;code&gt;ltr&lt;/code&gt;), setting it to &lt;code&gt;rtl&lt;/code&gt;. I’m also colouring the RTL text in green, just to make things super clear.&lt;/p&gt;

&lt;p&gt;I put together a simple &lt;code&gt;.Rmd&lt;/code&gt; file to test, &lt;code&gt;rtl.Rmd&lt;/code&gt;, rendered it into a message and then dispatched.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;envelope() %&amp;gt;%
  subject("Right-to-Left Text") %&amp;gt;%
  render("rtl.Rmd", css_files = "rtl.css")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extra CSS is included via the &lt;code&gt;css_files&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;🚨 If you want this to work nicely on Gmail then you’ll also need to set &lt;code&gt;include_css = c("rmd", "highlight")&lt;/code&gt; because Gmail doesn’t currently appreciate Bootstrap CSS.&lt;/p&gt;

&lt;p&gt;In the interests of brevity the contents of &lt;code&gt;rtl.Rmd&lt;/code&gt; have been appended to the end of the post.&lt;/p&gt;

&lt;p&gt;And this is what the delivered email looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--twOGZCLW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-right-to-left/rtl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--twOGZCLW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-right-to-left/rtl.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a &lt;code&gt;rtl&lt;/code&gt; class allowed me to mix both left-to-right and right-to-left content in the same message. In reality this is a rather unlikely requirement. To use RTL for the entire message just adapt the CSS as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  direction: rtl;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are other tweaks that I can make on &lt;code&gt;{emayili}&lt;/code&gt; to ensure that it supports your requirements, please raise &lt;a href="https://github.com/datawookie/emayili/issues"&gt;an issue&lt;/a&gt; on the repository.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e86W2s07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/datawookie/emayili/master/man/figures/emayili-hex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e86W2s07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/datawookie/emayili/master/man/figures/emayili-hex.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sclbRWdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fathomdata.dev/media/logo.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sclbRWdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fathomdata.dev/media/logo.svg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
The &lt;code&gt;{emayili}&lt;/code&gt; package is developed &amp;amp; supported by &lt;a href="https://www.fathomdata.dev"&gt;Fathom Data&lt;/a&gt;.&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;Here are the contents of &lt;code&gt;rtl.Rmd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
output: html_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r setup, include=FALSE}&lt;br&gt;
knitr::opts_chunk$set(echo = TRUE)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non magna
magna. Etiam eu sapien nulla. Cras ac lectus non urna scelerisque porttitor ac
non tortor.

&amp;lt;div class="rtl"&amp;gt;
צחוק המעבורת, מהדובים של הכרית החופשית. מי רוצה לפספס, עצוב בחצר האחורית, כד
רך. עכשיו הכאב של הכאב הוא לא הכאב של הטרדה, הצורך של אגמי החלבון של התפוז.
אלמנט כלשהו, ​​וגם לא יסוד כימי של נדל"ן, מראית עין של מגוון כלי רכב לנחות, רגע
לפני הספד החיים. אניאס הוא מחבר רכיב המחיר. אין פחד אלא אם להיות כנים. אני חי
במייסר העצוב של הכדורגל שלי, בסמך הזן כמובן. אבל המענה והקשתות, או קצות האורקים.
אל האגם או לפניו. הזמן של העמק לוקח. אפילו הפחד מהפחד, הקשתות או המעבורת, אתת
האינטרנט.
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r pressure, out.width="50%", fig.cap="כיתוב דמות", fig.class="rtl"}&lt;br&gt;
par(mar = c(5, 4, 1, 1))&lt;br&gt;
plot(pressure, xlab = "טֶמפֶּרָטוּרָה", ylab = "לַחַץ", mar = c(5, 4, 0, 2))&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;div class="rtl"&amp;gt;
המחלה נושאת את הסבירות לסוף השבוע, והפחד מהכדורגל קשה. אבל האריה והאריה והילדים
נמצאים במיטה. זה ברוטב צריך להגיד. אבל מבחינתו, צרות החיים מותירות הרבה חיים,
הקריקטורה זקוקה לארוסים. עכשיו אני שונא את הגורמים, לא את הכאב ולא את הסביבה,
הכניסה קלה. עכשיו הכניסה לחיי.
&amp;lt;/div&amp;gt;

Cras fringilla nunc in tellus sagittis accumsan. Mauris nisi tellus, congue sit
amet turpis nec, accumsan lacinia neque. Nulla facilisi.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>{emayili} Styling Figures</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Thu, 23 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-styling-figures-4i79</link>
      <guid>https://dev.to/datawookie/emayili-styling-figures-4i79</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wmMDDkTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wmMDDkTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/featured.jpg" alt="{emayili} Styling Figures"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags are wrapped in a tight &lt;code&gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt; embrace by &lt;code&gt;{knitr}&lt;/code&gt;. In general this works really well. However, I want to have more control over image formatting for &lt;code&gt;{emayili}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Hook
&lt;/h2&gt;

&lt;p&gt;I’d like to have the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags wrapped by &lt;code&gt;&amp;lt;figure&amp;gt;&amp;lt;/figure&amp;gt;&lt;/code&gt;. It’d also be useful to have the option of inserting a &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt;. To support these requirements I added a &lt;code&gt;{knitr}&lt;/code&gt; hook which adds in these tags. It responds to the following chunk parameters (all optional):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;out.width&lt;/code&gt; — the figure width&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fig.cap&lt;/code&gt; — figure caption&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fig.alt&lt;/code&gt; — figure alternative text and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fig.class&lt;/code&gt; — class attached to the &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;First we’ll try out the figure caption with the following &lt;code&gt;.Rmd&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
output: html_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r fig.cap="Mercury Vapour"}&lt;br&gt;
plot(pressure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendering this file, attaching to an &lt;code&gt;envelope&lt;/code&gt; object and sending it with &lt;code&gt;{emayili}&lt;/code&gt; yields the message below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RJHHOknK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-caption.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RJHHOknK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-caption.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing too exciting, just a caption neatly displayed below the plot.&lt;/p&gt;

&lt;p&gt;Next let’s try attaching a class to the &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag. We’ll use the CSS file &lt;code&gt;figures.css&lt;/code&gt;. This file needs to be specified with the &lt;code&gt;css_files&lt;/code&gt; argument to &lt;code&gt;render()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  margin: 30px 0px;
}

figure {
  margin-bottom: 20px;
}

figure.center {
  text-align: center;
}

figcaption {
  color: white;
  text-align: left;
  background-color: #9fa4a7;
  padding: 10px 20px;
}

figcaption::before {
  content: "Figure: ";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;.Rmd&lt;/code&gt; file we use the &lt;code&gt;fig.class&lt;/code&gt; parameter to apply the &lt;code&gt;center&lt;/code&gt; class to the &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
output: html_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r out.width="75%", fig.class="center"}&lt;br&gt;
plot(pressure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what the rendered message looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hddD3ijA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-class.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hddD3ijA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-class.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; is now centered in the &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally we’ll bring it all together by styling a figure caption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
output: html_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r out.width="75%", fig.class="center", fig.cap="Mercury Vapour"}&lt;br&gt;
plot(pressure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result in your inbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GW5IwZDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-caption-class.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GW5IwZDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-styling-figures/msg-caption-class.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since many automated reports use figures, the ability to style those figures should be very useful.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e86W2s07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/datawookie/emayili/master/man/figures/emayili-hex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e86W2s07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/datawookie/emayili/master/man/figures/emayili-hex.png" alt=""&gt;&lt;/a&gt; &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sclbRWdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fathomdata.dev/media/logo.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sclbRWdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fathomdata.dev/media/logo.svg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;



&lt;center&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
The &lt;code&gt;{emayili}&lt;/code&gt; package is developed &amp;amp; supported by &lt;a href="https://www.fathomdata.dev"&gt;Fathom Data&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
&lt;/center&gt;

</description>
    </item>
    <item>
      <title>{emayili} Managing CSS</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Wed, 22 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-managing-css-fib</link>
      <guid>https://dev.to/datawookie/emayili-managing-css-fib</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KIhQN_wR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KIhQN_wR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/featured.jpg" alt="{emayili} Managing CSS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I love the clean simplicity of an R Markdown document. But sometimes it can feel a little bare and utilitarian. This is especially the case if it’s rendered into the body of an email. How about injecting a little more pizzazz?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)

msg &amp;lt;- envelope()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  (In|Ex)cluding Rendered CSS
&lt;/h2&gt;

&lt;p&gt;By default the knitting process for an R Markdown document will inject CSS from &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; and &lt;a href="https://highlightjs.org/"&gt;highlightjs&lt;/a&gt;. When you &lt;code&gt;render()&lt;/code&gt; an R Markdown document in &lt;code&gt;{emayili}&lt;/code&gt; these CSS will be included in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the HTML document. This actually results in quite a lot of content, often more than contained in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; of the document.&lt;/p&gt;

&lt;p&gt;If you want to generate a more lightweight message then you can use &lt;code&gt;include_css&lt;/code&gt; option to turn off the CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;%
  # Do include rendered CSS (default).
  render("styling.Rmd", include_css = TRUE)

msg %&amp;gt;%
  # Don't include rendered CSS.
  render("styling.Rmd", include_css = FALSE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s take a look at those two options. This is what it looks like in Thunderbird when the rendered CSS is included.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sRY4A2PN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-include.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sRY4A2PN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-include.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is it without the rendered CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FA3p-3wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-exclude.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FA3p-3wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-exclude.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom CSS
&lt;/h2&gt;

&lt;p&gt;Now, what about including some custom CSS? Use the &lt;code&gt;css_files&lt;/code&gt; argument to specify one or more CSS files that will get baked into the message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;%
  render("styling.Rmd", include_css = FALSE, css_files = "styling.css")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the contents of &lt;code&gt;styling.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import url('https://fonts.googleapis.com/css2?family=Pacifico&amp;amp;display=swap');
@import url('https://fonts.googleapis.com/css2?family=Roboto&amp;amp;display=swap');

h2 {
  font-family: 'Pacifico', cursive;
}

p {
  font-family: 'Roboto', sans-serif;
}

body {
  background-color: grey;
  color: white;
  margin: 0 30px;
}

hr {
  height: 1px;
  background-color: white;
  border: none;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re pulling in some custom Google Fonts, changing the background colour of the page and adding in some margins, along with a few other tweaks.&lt;/p&gt;

&lt;p&gt;And this is what it looks like. I can’t claim to have any aesthetic sense at all. Sorry. I’m sure that you can do a lot better!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t8NkRes7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-custom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t8NkRes7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-managing-css/css-custom.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these options at your disposal there’s really no excuse for creating “ordinary” emails. Now’s your chance to be extra-ordinary.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{emayili} R Markdown Parameters</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Mon, 20 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-r-markdown-parameters-ahp</link>
      <guid>https://dev.to/datawookie/emayili-r-markdown-parameters-ahp</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e6haC1LX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-r-markdown-parameters/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e6haC1LX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-r-markdown-parameters/featured.jpg" alt="{emayili} R Markdown Parameters"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don’t frequently use parameters in R Markdown documents. So the initial implementation of &lt;code&gt;render()&lt;/code&gt; in &lt;code&gt;{emayili}&lt;/code&gt; did not cater for them. A small tweak makes it possible though.&lt;/p&gt;

&lt;p&gt;You can install the update from GitHub as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remotes::install_github("datawookie/emayili", ref = "v0.5.2")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then load the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)

# A couple of options to display message body.
#
options(
  envelope.details = TRUE,
  envelope.invisible = FALSE
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an empty message object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg &amp;lt;- envelope()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose you have the following simple R Markdown document, &lt;code&gt;params.Rmd&lt;/code&gt;, which you want to render into the body of an email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
title: "Message with Parameter"
output: html_document
params:
  value: 13
--------

The value is `r params$value`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll start by rendering it using the default parameter value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;%
  subject("Default parameter value") %&amp;gt;%
  render("params.Rmd", include_css = FALSE)

Date: Mon, 20 Sep 2021 08:20:59 GMT
Subject: Default parameter value
X-Mailer: {emayili}-0.5.2
MIME-Version: 1.0
Content-Type: multipart/related; boundary="9554b959"

--9554b959
Content-Type: text/html; charset=utf-8
Content-Disposition: inline

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div class="container-fluid main-container"&amp;gt;
&amp;lt;div id="header"&amp;gt;
&amp;lt;h1 class="title toc-ignore"&amp;gt;Message with Parameter&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;The value is 13.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

--9554b959--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, the default value for the parameter comes through.&lt;/p&gt;

&lt;p&gt;Now let’s test a different value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;%
  subject("Better parameter value") %&amp;gt;%
  render("params.Rmd", params = list(value = 42), include_css = FALSE)

Date: Mon, 20 Sep 2021 08:20:59 GMT
Subject: Better parameter value
X-Mailer: {emayili}-0.5.2
MIME-Version: 1.0
Content-Type: multipart/related; boundary="a099637d"

--a099637d
Content-Type: text/html; charset=utf-8
Content-Disposition: inline

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div class="container-fluid main-container"&amp;gt;
&amp;lt;div id="header"&amp;gt;
&amp;lt;h1 class="title toc-ignore"&amp;gt;Message with Parameter&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;The value is 42.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

--a099637d--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀 Success: the specified parameter value now appears. If you use parameters in your R Makrkdown documents, then you can be confident that these will now work in &lt;code&gt;{emayili}&lt;/code&gt; too.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{emayili} Rendering R Markdown</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Fri, 17 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-rendering-r-markdown-28m2</link>
      <guid>https://dev.to/datawookie/emayili-rendering-r-markdown-28m2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wh-S82UQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-r-markdown/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wh-S82UQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-r-markdown/featured.jpg" alt="{emayili} Rendering Plain Markdown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a &lt;a href="//../emayili-rendering-plain-markdown/"&gt;previous post&lt;/a&gt; I documented a new feature in &lt;code&gt;{emayili}&lt;/code&gt;, the ability to render Plain Markdown directly into the body of an email message.&lt;/p&gt;

&lt;p&gt;Today I’m announcing the release of a new minor version, 0.5.0, in which &lt;code&gt;{emayili}&lt;/code&gt; is now able to render R Markdown into an email. This is a major leap forward for the package.&lt;/p&gt;

&lt;p&gt;I should mention that this capability (rendering R Markdown into an email) is already available in the &lt;a href="https://cran.r-project.org/package=mailmerge"&gt;&lt;code&gt;{mailmerge}&lt;/code&gt; package&lt;/a&gt;, but that only works with Gmail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install &amp;amp; Load
&lt;/h2&gt;

&lt;p&gt;You can install this version directly from GitHub as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remotes::install_github("datawookie/emayili", ref = "v0.5.0")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version was also published on CRAN on 17 September 2021.&lt;/p&gt;

&lt;p&gt;Now load the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)

# A couple of options to display message body.
#
options(
  envelope_details = TRUE,
  envelope_invisible = FALSE
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check on the installed version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packageVersion("emayili")

[1] '0.5.0'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Simple R Markdown File
&lt;/h2&gt;

&lt;p&gt;For the purposes of illustration let’s use a super simple R Markdown file, &lt;code&gt;pi.Rmd&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--------
title: "Approximating Pi"
output: html_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r}&lt;br&gt;
22 / 7&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an empty message object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg &amp;lt;- envelope()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then render &lt;code&gt;pi.Rmd&lt;/code&gt; into the body of the message. In this case I’m going to set the &lt;code&gt;include_css&lt;/code&gt; option to &lt;code&gt;FALSE&lt;/code&gt; so that the copious volume of CSS is suppressed in the resulting HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;% render("pi.Rmd", include_css = FALSE)

Date: Fri, 17 Sep 2021 10:16:09 GMT
X-Mailer: {emayili}-0.5.0
MIME-Version: 1.0
Content-Type: multipart/related; boundary="b9173ea5"

--b9173ea5
Content-Type: text/html; charset=utf-8
Content-Disposition: inline

&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;
&amp;lt;div class="container-fluid main-container"&amp;gt;
&amp;lt;div id="header"&amp;gt;
&amp;lt;h1 class="title toc-ignore"&amp;gt;Approximating Pi&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;pre class="r"&amp;gt;&amp;lt;code&amp;gt;22 / 7&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;[1] 3.142857&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;

--b9173ea5--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The document is inserted as a separate MIME chunk with type &lt;code&gt;text/html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s move onto a more interesting example.&lt;/p&gt;

&lt;h2&gt;
  
  
  R Markdown File from Template
&lt;/h2&gt;

&lt;p&gt;Create an R Markdown file using the &lt;code&gt;"github_document"&lt;/code&gt; template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rmd &amp;lt;- "gh-doc.Rmd"

rmarkdown::draft(
  rmd,
  template = "github_document",
  package = "rmarkdown",
  edit = FALSE
)

--------
title: "Untitled"
output: github_document
--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r setup, include=FALSE}&lt;br&gt;
knitr::opts_chunk$set(echo = TRUE)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## GitHub Documents

This is an R Markdown format used for publishing markdown documents to GitHub. 
When you click the **Knit** button all R code chunks are run and a markdown 
file (.md) suitable for publishing to GitHub is generated.

## Including Code

You can include R code in the document as follows:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r cars}&lt;br&gt;
summary(cars)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Including Plots

You can also embed plots, for example:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
{r pressure, echo=FALSE}&lt;br&gt;
plot(pressure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Note that the `echo = FALSE` parameter was added to the code chunk to prevent 
printing of the R code that generated the plot.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll add some addresses to the message object and render the R Markdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;msg %&amp;gt;%
  to("bob@google.com") %&amp;gt;%
  from("alice@google.com") %&amp;gt;%
  subject("Rendering an R Markdown Document") %&amp;gt;%
  render(rmd)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dispatching the email results in the message below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hQNhiLdZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-r-markdown/gh-doc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hQNhiLdZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-r-markdown/gh-doc.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The message doesn’t need to look this generic. You can make your R Markdown documents as fancy as you want and &lt;code&gt;{emayili}&lt;/code&gt; will be happy to send them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you haven’t tried &lt;code&gt;{emayili}&lt;/code&gt; before, now is a great time to give it a shot. And if you use R for automated reporting, then being able to render R Markdown directly into an email should simplify your workflow.&lt;/p&gt;

&lt;p&gt;I gave a talk about these new features (and most of the old features!) at the Birmingham R User Group. The slides are available &lt;a href="https://fathomdata.gitlab.io/library/presentation/20210916-birminghamr-meetup/20210916-birminghamr-emayili.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{emayili} Rendering Plain Markdown</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Fri, 10 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-rendering-plain-markdown-5aa3</link>
      <guid>https://dev.to/datawookie/emayili-rendering-plain-markdown-5aa3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MFeaL5MZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-plain-markdown/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MFeaL5MZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-rendering-plain-markdown/featured.jpg" alt="{emayili} Rendering Plain Markdown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve been able to attach text and HTML content to messages with &lt;code&gt;{emayili}&lt;/code&gt;. But something that I’ve really been wanting to do is render &lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;markdown&lt;/a&gt; directly into an email.&lt;/p&gt;

&lt;p&gt;In version 0.4.19 I’ve added the ability to directly render plain markdown into a message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;render()&lt;/code&gt; method will handle plain markdown either as a character vector or from a file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown String
&lt;/h2&gt;

&lt;p&gt;Let’s start with markdown in a character vector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email &amp;lt;- envelope() %&amp;gt;%
  render("[This](https://www.google.com) is a link.")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s the raw email document look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(email, TRUE)

Date: Fri, 10 Sep 2021 03:42:47 GMT
X-Mailer: {emayili}-0.4.19
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="e28a25a2a39253440353e12c623"

--e28a25a2a39253440353e12c623
Content-Type: text/html; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

&amp;lt;p&amp;gt;&amp;lt;a href=3D"https://www.google.com"&amp;gt;This&amp;lt;/a&amp;gt; is a link.&amp;lt;/p&amp;gt;

--e28a25a2a39253440353e12c623--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The markdown has been translated into HTML and inserted as a &lt;code&gt;text/html&lt;/code&gt; MIME element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown in a File
&lt;/h2&gt;

&lt;p&gt;You’re more likely to have your markdown in a file. Suppose that the file &lt;code&gt;message.md&lt;/code&gt; contains the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[This](https://www.google.com) is a link.

- One
- Two
- Three

![](https://cran.r-project.org/Rlogo.svg)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;render()&lt;/code&gt; function will identify its argument as a file path and render the contents of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email &amp;lt;- envelope() %&amp;gt;%
  render("message.md")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what the resulting MIME message looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(email, TRUE)

Date: Fri, 10 Sep 2021 03:42:47 GMT
X-Mailer: {emayili}-0.4.19
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="8301b826a4415341f24323d192f"

--8301b826a4415341f24323d192f
Content-Type: text/html; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

&amp;lt;p&amp;gt;&amp;lt;a href=3D"https://www.google.com"&amp;gt;This&amp;lt;/a&amp;gt; is a link.&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;One&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Two&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Three&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;img src=3D"https://cran.r-project.org/Rlogo.svg" alt=3D"" /&amp;gt;&amp;lt;/p&amp;gt;

--8301b826a4415341f24323d192f--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new feature partially scratches a persistent itch. But I’ll only be satisfied when I can render R markdown straight into an email. Stay tuned.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{clockify} Time Tracking from R</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Thu, 09 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/clockify-time-tracking-from-r-42ip</link>
      <guid>https://dev.to/datawookie/clockify-time-tracking-from-r-42ip</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--znUcitQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/clockify-time-tracking-from-r/featured.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--znUcitQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/clockify-time-tracking-from-r/featured.png" alt="{clockify} — Time Tracking from R"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://fathomdata.dev/"&gt;Fathom Data&lt;/a&gt; we use &lt;a href="https://clockify.me/"&gt;Clockify&lt;/a&gt; to keep detailed records of the time that we spend working on our clients’ projects. Up until fairly recently we manually generated timesheets at the end of each month that were sent through to the clients along with their invoices. Our experience has been that providing detailed timesheets helps foster trust and transparency. However, with a growing team and an expanding clientele, generating these timesheets has become progressively more laborious. Time to automate!&lt;/p&gt;

&lt;p&gt;Luckily Clockify exposes an extensive API. I built a small R package, &lt;a href="https://github.com/datawookie/clockify"&gt;{clockify}&lt;/a&gt; which is a wrapper around the &lt;a href="https://clockify.me/developers-api"&gt;Clockify API&lt;/a&gt;, making it easy to tap into our timesheet data. We’re then using R Markdown to generate some pretty sweet automated timesheets and reports. This reporting all happens automatically courtesy of GitLab CI. But that’s a story for another day. Let’s take a look at &lt;code&gt;{clockify}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This post documents &lt;code&gt;{clockify}-0.0.9&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;You can install the latest development version straight from GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remotes::install_github("datawookie/clockify")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can install a &lt;a href="https://cran.r-project.org/package=clockify"&gt;stable version from CRAN&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install.packages("clockify")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package documentation is available &lt;a href="https://datawookie.github.io/clockify/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you’ve installed the package, load it into R and check the version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(clockify)

packageVersion("clockify")

[1] '0.0.9'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Key
&lt;/h2&gt;

&lt;p&gt;You’re going to need to have an API key from your Clockify account. If you don’t yet have an account, create one. Then retrieve the API key from the &lt;a href="https://clockify.me/user/settings"&gt;account settings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Set the API key to be used for the session. I store my API key in an environment variable called &lt;code&gt;CLOCKIFY_API_KEY&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLOCKIFY_API_KEY = Sys.getenv("CLOCKIFY_API_KEY")

set_api_key(CLOCKIFY_API_KEY)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s turn on logging so we can see what’s happening behind the scenes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(logger)

log_threshold(DEBUG)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Workspaces
&lt;/h2&gt;

&lt;p&gt;Clockify uses workspaces to group people and projects. Every Clockify account is automatically associated with a default workspace, but may be linked to other workspaces too.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;workspaces()&lt;/code&gt; to retrieve a list of available workspaces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspaces()

2021-09-09 08:21:32 — GET /workspaces

# A tibble: 2 × 2
  workspace_id name       
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt;      
1 5ef46294df73063139f60bfc Fathom Data
2 61343c45ab05e02be2c8c1fd Personal   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have two workspaces associated with my account, &lt;code&gt;Personal&lt;/code&gt; and &lt;code&gt;Work&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Select the &lt;code&gt;Personal&lt;/code&gt; workspace. All subsequent operations will apply within this workspace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace("61343c45ab05e02be2c8c1fd")

2021-09-09 08:21:32 — Set active workspace -&amp;gt; 61343c45ab05e02be2c8c1fd.

[1] "61343c45ab05e02be2c8c1fd"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Users
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;users()&lt;/code&gt; function generates a table of all users linked to the workspace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users()

2021-09-09 08:21:32 — GET /workspaces/61343c45ab05e02be2c8c1fd/users

# A tibble: 2 × 3
  user_id user_name status
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; 
1 5f227e0cd7176a0e6e754409 Andrew ACTIVE
2 5ef46293df73063139f60bf5 Emma ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve information from your user profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user()

2021-09-09 08:21:32 — GET /user

# A tibble: 1 × 3
  user_id user_name status
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; 
1 5f227e0cd7176a0e6e754409 Andrew ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clients
&lt;/h2&gt;

&lt;p&gt;So, who do you work for? Let’s get a list of clients associated with the workspace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clients()

2021-09-09 08:21:33 — GET /workspaces/61343c45ab05e02be2c8c1fd/clients

# A tibble: 2 × 3
  client_id workspace_id client_name
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt;      
1 61343c6c00dc8f48962b9be9 61343c45ab05e02be2c8c1fd Community  
2 61343c5d00dc8f48962b9be3 61343c45ab05e02be2c8c1fd Fathom Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Projects
&lt;/h2&gt;

&lt;p&gt;Get a table of projects. The endpoint used to retrieve projects is paginated, so there are multiple API calls to retrieve the complete list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projects()

2021-09-09 08:21:33 — GET /workspaces/61343c45ab05e02be2c8c1fd/projects
2021-09-09 08:21:33 — Page contains 2 results.
2021-09-09 08:21:33 — GET /workspaces/61343c45ab05e02be2c8c1fd/projects
2021-09-09 08:21:33 — Page is empty.
2021-09-09 08:21:33 — API returned 2 results.

# A tibble: 2 × 4
  project_id project_name client_id billable
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;lgl&amp;gt;   
1 6134506c777d5361dcdeb3b5 {cex} 61343c5d00dc8f48962b9be3 TRUE    
2 61343c9ba15c1d53ad33369f {clockify} 61343c5d00dc8f48962b9be3 FALSE   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m logging time on two projects, &lt;a href="https://github.com/datawookie/clockify"&gt;{clockify}&lt;/a&gt; and &lt;a href="https://github.com/datawookie/emayili"&gt;{emayili}&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s change the logging threshold, because it’s going to get noisy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log_threshold(INFO)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Time Entries
&lt;/h2&gt;

&lt;p&gt;The really interesting data is captured in the time entries: how much time am I spending on each of the projects and what am I doing?&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieve Time Entries
&lt;/h3&gt;

&lt;p&gt;Retrieve the time entries for the authenticated user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entries()

# A tibble: 8 × 4
  id project_id description duration
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;dbl&amp;gt;
1 61343cc1777d5361dcdea70a 61343c9ba15c1d53ad33369f Set up GitHub Acti… 12  
2 61343d06777d5361dcdea729 61343c9ba15c1d53ad33369f Make coffee 5  
3 61343d27ab05e02be2c8c266 61343c9ba15c1d53ad33369f Populate README.Rmd 68  
4 613448d7777d5361dcdead37 61343c9ba15c1d53ad33369f Add GET /workspace… 12.0
5 61344bcad01d3b4a27a82310 61343c9ba15c1d53ad33369f Add GET /workspace… 31.8
6 6134548f00dc8f48962bace7 6134506c777d5361dcdeb3b5 Add GET /ticker/{s… 16.0
7 6134585a777d5361dcdebc5c 6134506c777d5361dcdeb3b5 Add GET /tickers/{… 16.5
8 61345c45d01d3b4a27a833c7 6134506c777d5361dcdeb3b5 Add GET /last_pric… 31.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get the start and end times (as well as a bunch of other fields) for each entry by setting &lt;code&gt;concise = FALSE&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entries(concise = FALSE) %&amp;gt;% select(id, starts_with("time"))

# A tibble: 8 × 3
  id time_start time_end           
  &amp;lt;chr&amp;gt; &amp;lt;dttm&amp;gt; &amp;lt;dttm&amp;gt;             
1 61343cc1777d5361dcdea70a 2021-09-03 05:15:00 2021-09-03 05:27:00
2 61343d06777d5361dcdea729 2021-09-03 05:27:00 2021-09-03 05:32:00
3 61343d27ab05e02be2c8c266 2021-09-03 05:45:00 2021-09-03 06:53:00
4 613448d7777d5361dcdead37 2021-09-05 04:34:31 2021-09-05 04:46:30
5 61344bcad01d3b4a27a82310 2021-09-05 04:47:06 2021-09-05 05:18:56
6 6134548f00dc8f48962bace7 2021-09-05 05:24:30 2021-09-05 05:40:29
7 6134585a777d5361dcdebc5c 2021-09-05 05:40:42 2021-09-05 05:57:13
8 61345c45d01d3b4a27a833c7 2021-09-05 05:57:25 2021-09-05 06:29:01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve time entries for another user specified by their user ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entries(user_id = "5ef46293df73063139f60bf5")

# A tibble: 2 × 4
  id project_id description duration
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;dbl&amp;gt;
1 613630ebba4b374e57155a72 61343c9ba15c1d53ad33369f Another test entry 87  
2 613630cb89516b1767a56a08 61343c9ba15c1d53ad33369f Creating hex logo 45.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Insert Time Entry
&lt;/h3&gt;

&lt;p&gt;What about inserting a new time entry? No problem, call &lt;code&gt;time_entry_insert()&lt;/code&gt; and supply the details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prepare_cran_id &amp;lt;- time_entry_insert(
 project_id = "61343c9ba15c1d53ad33369f",
 start = "2021-08-30 08:00:00",
 end = "2021-08-30 10:30:00",
 description = "Prepare for CRAN submission"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID for the newly inserted time entry is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prepare_cran_id

[1] "6139c40ff1d18b7ed667a7bd"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm that it has been inserted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entries(concise = FALSE) %&amp;gt;% select(id, description, duration)

# A tibble: 9 × 3
  id description duration
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;dbl&amp;gt;
1 6139c40ff1d18b7ed667a7bd Prepare for CRAN submission 150  
2 61343cc1777d5361dcdea70a Set up GitHub Actions 12  
3 61343d06777d5361dcdea729 Make coffee 5  
4 61343d27ab05e02be2c8c266 Populate README.Rmd 68  
5 613448d7777d5361dcdead37 Add GET /workspaces/{workspaceId}/projects/… 12.0
6 61344bcad01d3b4a27a82310 Add GET /workspaces/{workspaceId}/projects/… 31.8
7 6134548f00dc8f48962bace7 Add GET /ticker/{symbol1}/{symbol2} 16.0
8 6134585a777d5361dcdebc5c Add GET /tickers/{symbol1}/{symbol2}/.../{s… 16.5
9 61345c45d01d3b4a27a833c7 Add GET /last_price/{symbol1}/{symbol2} 31.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete Time Entry
&lt;/h3&gt;

&lt;p&gt;What about deleting a time entry? Easy, just call &lt;code&gt;time_entry_delete()&lt;/code&gt; and supply the time entry ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entry_delete(prepare_cran_id)

[1] TRUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm that it has been deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time_entries(concise = FALSE) %&amp;gt;% select(id, description, duration)

# A tibble: 8 × 3
  id description duration
  &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;dbl&amp;gt;
1 61343cc1777d5361dcdea70a Set up GitHub Actions 12  
2 61343d06777d5361dcdea729 Make coffee 5  
3 61343d27ab05e02be2c8c266 Populate README.Rmd 68  
4 613448d7777d5361dcdead37 Add GET /workspaces/{workspaceId}/projects/… 12.0
5 61344bcad01d3b4a27a82310 Add GET /workspaces/{workspaceId}/projects/… 31.8
6 6134548f00dc8f48962bace7 Add GET /ticker/{symbol1}/{symbol2} 16.0
7 6134585a777d5361dcdebc5c Add GET /tickers/{symbol1}/{symbol2}/.../{s… 16.5
8 61345c45d01d3b4a27a833c7 Add GET /last_price/{symbol1}/{symbol2} 31.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;

&lt;p&gt;I’ve started writing unit tests. You can check on the current test coverage &lt;a href="https://app.codecov.io/gh/datawookie/clockify"&gt;here&lt;/a&gt;. Evidently there’s still work to be done!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y_VUNE_K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/datawookie/clockify/raw/master/man/figures/clockify-hex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y_VUNE_K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/datawookie/clockify/raw/master/man/figures/clockify-hex.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Exposing an API adds a lot of value to Clockify as a time tracking product. If you’re using Clockify, then you’ll find the &lt;code&gt;{clockify}&lt;/code&gt; package convenient and useful for accessing those data. It plays particularly well in automated reporting scenarios.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up a Tiny HTTP Proxy</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Wed, 08 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/setting-up-a-tiny-http-proxy-3339</link>
      <guid>https://dev.to/datawookie/setting-up-a-tiny-http-proxy-3339</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TxSIjvGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/setting-up-a-tiny-http-proxy/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TxSIjvGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/setting-up-a-tiny-http-proxy/featured.jpg" alt="Setting up a Tiny HTTP Proxy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s often handy to have access to a HTTP proxy. I use this recipe from time to time to quickly fling together a proxy server which I can use to relay HTTP requests from a different origin.&lt;/p&gt;

&lt;h2&gt;
  
  
  EC2 Instance
&lt;/h2&gt;

&lt;p&gt;Create an EC2 instance in the region you want the proxy to reside. You can go with the smallest instance available. I’m using a &lt;code&gt;t3.nano&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install an Ubuntu AMI.&lt;/p&gt;

&lt;p&gt;If you are planning on keeping the proxy running indefinitely, then it will be a good idea to associate an Elastic IP address with this instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Tinyproxy
&lt;/h2&gt;

&lt;p&gt;Connect to the EC2 instance via SSH and install &lt;a href="https://tinyproxy.github.io/"&gt;Tinyproxy&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install tinyproxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restrict Access
&lt;/h2&gt;

&lt;p&gt;Now you probably don’t want the proxy to be accessible to everybody, so lock it down to a specific selection of IP addresses.&lt;/p&gt;

&lt;p&gt;Find the IP address of the machine that will have access to the proxy. You can either visit &lt;a href="https://whatismyipaddress.com/"&gt;https://whatismyipaddress.com/&lt;/a&gt; or run the following shell command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl ipecho.net/plain

3.91.59.140
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a security group which allows access from this IP on port 8888.&lt;/p&gt;

&lt;p&gt;You could be a lot more permissive and allow access on port 8888 from all IP addresses. But this might end in tears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Tinyproxy
&lt;/h2&gt;

&lt;p&gt;Edit the configuration for Tinyproxy at &lt;code&gt;/etc/tinyproxy/tinyproxy.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allowing Access from Nominated IP Addresses
&lt;/h3&gt;

&lt;p&gt;Find the &lt;code&gt;Allow&lt;/code&gt; section in the configuration file and add a line for the machine which will be accessing the proxy. Specify the IP address that you found earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allow 3.91.59.140
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting should be consistent with whatever access you have permitted in the security group.&lt;/p&gt;

&lt;p&gt;An alternative (and possibly more flexible) approach is to not define any &lt;code&gt;Allow&lt;/code&gt; rules but handle access via security groups.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UfYPL_3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/img/2020/06/tinyproxy-inbound-rules.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UfYPL_3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/img/2020/06/tinyproxy-inbound-rules.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to live dangerously then you can allow access from anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allow 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be warned though, this too may end in tears.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Authentication
&lt;/h3&gt;

&lt;p&gt;What about ensuring that only authenticated users have access to the service? No problem, just add a &lt;code&gt;BasicAuth&lt;/code&gt; entry to the configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BasicAuth alice izlGVukLF8bSQuuzZKg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restart
&lt;/h2&gt;

&lt;p&gt;After the configuration has been adjusted, restart Tinyproxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo service tinyproxy restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;Now test the proxy. Suppose, for example, that the proxy is running on a machine with IP address 3.11.245.24.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Access HTTP(S) site via proxy (assuming no authentication required).
curl --proxy 3.11.245.24:8888 http://ipecho.net/plain
curl --proxy 3.11.245.24:8888 https://ipecho.net/plain

# Provide credentials for basic authentication.
curl --proxy alice:izlGVukLF8bSQuuzZKg@3.11.245.24:8888 http://ipecho.net/plain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In each case you should get back the IP address of the proxy server.&lt;/p&gt;

&lt;p&gt;Once you’ve confirmed that it works, add it to the system or browser settings. You’re sorted! 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lxGkZ-XR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/setting-up-a-tiny-http-proxy/network-proxy-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lxGkZ-XR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/setting-up-a-tiny-http-proxy/network-proxy-config.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>{emayili} Interpolating Message Content</title>
      <dc:creator>Andrew B. Collier</dc:creator>
      <pubDate>Fri, 03 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/datawookie/emayili-interpolating-message-content-2cj</link>
      <guid>https://dev.to/datawookie/emayili-interpolating-message-content-2cj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C_E4rnUR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-interpolating-message-content/featured.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C_E4rnUR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://datawookie.dev/blog/2021/09/emayili-interpolating-message-content/featured.jpg" alt="{emayili} Interpolating Message Content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A small new feature added to &lt;a href="https://github.com/datawookie/emayili"&gt;&lt;code&gt;{emayili}&lt;/code&gt;&lt;/a&gt;: the ability to interpolate content into the email message body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Load Package
&lt;/h2&gt;

&lt;p&gt;Load the &lt;code&gt;{emayili}&lt;/code&gt; package and create a message skeleton.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;library(emayili)

options(
  # Always print message body.
  envelope_details = TRUE,
  # Print message from pipeline.
  envelope_invisible = FALSE
)

# Create a message skeleton.
#
email &amp;lt;- envelope()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Body
&lt;/h2&gt;

&lt;p&gt;Now let’s look at different ways to add a text body.&lt;/p&gt;

&lt;p&gt;If you know precisely what your message is going to say, then you can just write it straight into the message body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email %&amp;gt;% text("Hello Alice!")

Date: Fri, 03 Sep 2021 09:07:10 GMT
X-Mailer: {emayili}-0.4.17
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="1133f2d1f303af212e1f293e2c361c"

--1133f2d1f303af212e1f293e2c361c
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

Hello Alice!

--1133f2d1f303af212e1f293e2c361c--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if a part of the message is stored in a variable? Use the &lt;code&gt;{glue}&lt;/code&gt; syntax for variable interpolation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name &amp;lt;- "Bob"

email %&amp;gt;% text("Hello {name}!")

Date: Fri, 03 Sep 2021 09:07:10 GMT
X-Mailer: {emayili}-0.4.17
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="1133f2d1f303af212e1f293e2c361c"

--1133f2d1f303af212e1f293e2c361c
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

Hello Bob!

--1133f2d1f303af212e1f293e2c361c--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should make generating automated (yet personalised!) emails a lot simpler. For example, here’s how you’d go about creating emails from a data frame with columns holding names and email addresses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tribble(
  ~email, ~name,
  "alice@google.com", "Alice",
  "bob@yahoo.com", "Bob"
) %&amp;gt;%
  pwalk(function(email, name) {
    email &amp;lt;- envelope(to = email) %&amp;gt;% text("Hello {name}!")
    print(email)
  })

Date: Fri, 03 Sep 2021 09:07:10 GMT
To: alice@google.com
X-Mailer: {emayili}-0.4.17
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="153ce203c1ff10232f22237eb5"

--153ce203c1ff10232f22237eb5
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

Hello Alice!

--153ce203c1ff10232f22237eb5--
Date: Fri, 03 Sep 2021 09:07:10 GMT
To: bob@yahoo.com
X-Mailer: {emayili}-0.4.17
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="92832121424fb5133d2a1220371b"

--92832121424fb5133d2a1220371b
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

Hello Bob!

--92832121424fb5133d2a1220371b--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unleash your inner spam king.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
