4

Ultimately my objective is to prevent email tracking via auto-loaded images with unique links. I'm aware that Google uses a proxy to load the images so at least they won't reveal my IP address, but there are certain individuals and organizations that annoyingly embed trackers into their emails -- and they actively check whether or not I read their emails.

In the Gmail app specifically, I can turn off auto-loading of images, but I'd like to have similar protection for other apps that don't have this setting. For example, apparently there isn't a way to do this with Google Inbox.

My current thought is to write a back-end script that can run on new mail (received either from an event or frequent polling) to turn embedded HTML images into hyperlinks links to those images -- only used if I really need the image. That way, no matter what app I use to open the email, I'm in control of how/when I'm tracked. Editing emails is something that I've done with the MS Exchange Server APIs, and I'm looking for a way to do this with Gmail -- by whatever means available.

I found a couple of threads from 2010 on how to modify the subject line using Google Apps Script and Gmail itself. At the time, you couldn't do that, but it seems possible that these have been updated since then or that there are solutions using the Gmail API or IMAP.

tl;dr

For my Gmail account, how can I programmatically modify (and save changes to) received emails?

Possible solutions:

  • Google Apps scripts
  • Gmail API
  • IMAP
  • Other?
Community
  • 1
  • 1
Logical Fallacy
  • 3,017
  • 5
  • 25
  • 41

1 Answers1

7

I think the Gmail API would suit your needs perfectly.

Let's say I'm polling my Inbox every minute for new messages, with the Users.messages.list()-request. I'm careful to use the after-parameter in my query with the value of the last time I checked my Inbox, as seconds since the epoch. I only ask for Ids of the potential new messages. You could also subscribe to push events to mitigate the risk of your user pressing the message before you poll and alter the messages, as mentioned by @Max in the comments. Probably not an issue if the script is just for you.

q = after:<TIME_IN_SECONDS_SINCE_EPOCH_OF_LAST_POLL>
fields = messages/id

GET https://www.googleapis.com/gmail/v1/users/me/messages?fields=messages%2Fid&q=after%3A1437677475478&access_token={YOUR_API_KEY}

Response:

{
 "messages": [
    {
     "id": "14ebc16800d1fdc0"
    }, ...
  ]
}

Ha! I have a new message. I get it raw, decode its URL safe base64-encoded content, and have a look.

format = raw
fields = raw

GET https://www.googleapis.com/gmail/v1/users/me/messages/14eb68cb028163ba?fields=raw&format=raw&access_token={YOUR_API_KEY}

Response:

{
 "raw": "RGVsaXZlcmVk..."
}

Let's do the aforementioned base64-decoding. Replace all the "-" with "+", and "_" with "/" to transform it from URL safe base64 data to regular base64-encoded data.

atob("RGVsaXZlcmVk...".replace(/\-/g, '+').replace(/\_/g, '/'));

Result:

<html lang="en">
<head>
<title>
Computerphile just uploaded a video
</title>

.
.
.


<img class="open_tracking_img" src="http://www.youtube.com/attribution_link?a=vi-KC3YA0Qc&u=/gen_204%3Fa%3Dem-uploademail" width="1" height="1">

.
.
.
</html>

Contains a lot of img-tags, for sure.

I just extract the img-tags, get the URLs, and remove all the img-tags in the mail with my favourite XML Parser.

After the tags are removed, I just insert the URLs in the mail where I see fit, and encode it back to the URL safe base64-encoded data it was retrieved in.

btoa("<html lang="en">...".replace(/\+/g, '-').replace(/\//g, '_'));

Finally, I delete the original mail and insert the modified one.

DELETE https://www.googleapis.com/gmail/v1/users/me/messages/14eb68cb028163ba?access_token={YOUR_API_KEY}

POST https://www.googleapis.com/gmail/v1/users/me/messages?access_token={YOUR_API_KEY}

{
 "raw": "RGVsaXZlcmVkLVRvO..."
}

My new, modified mail is now in the inbox instead!

enter image description here

Community
  • 1
  • 1
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • Major race condition though.... Hope your user doesn't click on the original one before your delete propagates to them, which can take quite some time for mobile clients. – Max Jul 23 '15 at 19:56
  • @Max That is true :) Added edit for push notifications. – Tholle Jul 23 '15 at 19:59
  • 1
    This looks like a great solution. In addition, I need to keep track of which emails I've sanitized so I'll probably tag all incoming mail with an "unsanitary" label (using a standard Gmail filter) and remove the label with my script when it's done. If possible, I'm going to send those messages to trash instead of using DELETE, just in case my script ends up corrupting an email message. – Logical Fallacy Jul 24 '15 at 15:45
  • @ElephantsonParade That sounds great! Yeah, putting them in trash is probably a lot safer. :) – Tholle Jul 24 '15 at 16:03