MattBagg MattBagg - 1 year ago 159
R Question

Adding an attachment with gmailr

I am having difficulty using the r package

(version 0.7.1) to attach a file to my email without obscuring the text/body of the email. What, if anything, is wrong with the resulting MIME email, shown below.

This works and the text/body reading "This is a test..." is visible in the e-mail:

pkgs <- c('purrr','gmailr')
lapply(pkgs, require, character.only = T)

mime() %>%
to("") %>%
from("") %>%
text_body("This is a test of attaching a file to a messsage!") %>%
subject("Testing") -> text_msg


This, on the other hand, will attach the pdf but the text of the message is no longer visible:

mime() %>%
to("") %>%
from("") %>%
text_body("This is a test of attaching a file to a messsage!") %>%
subject("Testing") %>%
attach_file("some.pdf") -> text_msg


Although gmail does not display the text, if I view the source of the email I can see the text/body:

Received: from 955034766742
named unknown
Tue, 9 Aug 2016 20:15:25 -0400
MIME-Version: 1.0
Date: Tue, 9 Aug 2016 20:15:25 -0400
Subject: Testing
Content-Type: multipart/mixed; boundary=ae1edfec3113c0631c2b52e180caeb61
Content-Disposition: inline
Message-Id: <>

MIME-Version: 1.0
Date: Wed, 10 Aug 2016 00:21:29 GMT
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

This is a test of attaching a file to a messsage!
MIME-Version: 1.0
Date: Wed, 10 Aug 2016 00:21:29 GMT
Content-Type: application/pdf; name=some.pdf
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename=some.pdf; modification-date=Mon, 08 Aug 2016 18:40:49 GMT


Is the MIME somehow malformed? Why does the second MIME section not display?

Answer Source

The issue seems to be that gmailr is not including an initial MIME boundary line before the body of the text. MIME (Multipurpose Internet Mail Extensions) is the standard for email. When there are multiple parts to an email, as when you attach a file, the standard specifies that each part is preceded by an encapsulation boundary, which is an arbitrary string defined in the header (on the 11th line of the example above).

In the email example above, there is no boundary line before the body begins on line 15. The area before the first boundary line is supposed to be ignored by MIME-compliant clients and is traditionally used to put a message to users of old non-MIME clients. This may be why gmail still finds the body of the text and displays it if there if no attachment but does not display it when an attachment is present.

My ugly fix for this is to alter the gmailr::send_message function to insert the initial boundary line. This is obviously a worse fix than fixing the function with which gmailr inserts boundary lines and for that I apologize.

send_message_fixed <- function (mail, type = c("multipart", "media", "resumable"), 
          thread_id = NULL, user_id = "me") 
  mail <- as.character(mail)
  # infer boundary line
  boundary_line <- paste0(stringr::str_replace(stringr::str_extract(mail,"boundary=.{1,}"),"boundary=","--"),"\r\n")
  # find where to insert it
  need_boundary_before_here <- stringr::str_locate_all(mail,"MIME")[[1]][2,1]
  # insert it
  mail <- paste0(substr(mail,1,need_boundary_before_here-1),
  stopifnot(gmailr:::nullable(gmailr:::is_string)(thread_id), gmailr:::is_string(user_id))
  type <- match.arg(type)
  gmailr:::gmailr_POST(c("messages", "send"), user_id, class = "gmail_message", 
              query = list(uploadType = type), 
              body = jsonlite::toJSON(auto_unbox = TRUE, 
                                      null = "null", c(threadId = thread_id, 
                                                       list(raw = gmailr:::base64url_encode(mail)))), 
                                      httr::add_headers(`Content-Type` = "application/json"))