I’m actually surprised nobody has posted anything about this online, but it’s more or less possible to write an IMAP proxy for Facebook using their API. Sure, you could not write a full-fledge IMAP implementation, but you could get it to perform the most basic tasks, like reading messages (and therefore also store them locally).
I got the idea of making an IMAP proxy (or bridge, parser or whatever you want to call it) back in in August when Facebook announced new API calls to access the mailbox. Upon reading them, I realized that it could be used for a lot new things, such as this. Since then, I’ve been waiting for someone take on the challenge. I’ve been keeping an eye open for a projects in this area, but haven’t seen any. Therefore, I thought I might at least raise some interest and perhaps someone will take on the challenge.
Please note that this is in no way a complete guide on how to write this software, nor am I sure that it works — it just outlines some of the basic concept I’ve had on my mind for a while.
In this article I’ll try to outline the basics of writing this software. There are primarily three API calls that we need to work with to accomplish this:
Logging in might be one of the trickier parts. The way you login to an IMAP server is to use the following command:
LOGIN username password
Facebook’s API is not quite as simplistic. I’m not quite sure what the best approach is here, but one way would be to have two-sided service. The first part is a web-interface. In order to use the proxy/bridge, you’d need to visit the website and approve the service using Facebook Connect (it would also require something called Extended Permissions as pointed out in the documentation for the Message API).
Once you have approved the web service using the web-interface, you would be able to connect to the second part of the equation, namely the IMAP server. Since you have previously approved the web-service, you should be able to replicate the IMAP login command above.
One important feature in IMAP is the ability to list folders. Luckily, Facebook’s structure looks very similar to a minimalistic IMAP setup. However, since you cannot add or delete folders in Facebook’s messaging system (yet), we can fake this response (since the structure is always the same).
The command you’d issue to the IMAP server is:
LIST "" *
Since we know from the Mailbox folder API description, there are only three folders. These folders are:
- Inbox (folder_id 0)
- Outbox (folder_id 1)
- Updates (folder_id 4)
With this in mind, we can hard code the following response to the list command:
LIST (\HasChildren) "/" Inbox
LIST (\HasChildren) "/" Outbox
LIST (\HasChildren) "/" Updates
Selecting a folder
In IMAP, you use the following command to select a folder:
Given Facebook’s query-based API, the closest thing to ‘selecting a folder’ in Facebook’s API is to add ‘WHERE folder_id = 0′ to the query. One way to implement this is if the server stores the latest SELECT command and automatically appends ‘WHERE folder_id = 0′ to the end of each query (since 0 is for Inbox, we’d append this string if the users the user previously issued ‘SELECT Inbox’)
Getting message content
The key in getting messages trough IMAP is a command named FETCH. The FETCH command comes with a lot of different modes, but we’ll focus on the most fundamental ones. These are (AFAIK) FLAGS, RFC822, RFC822.HEADER, RFC822.TEXT, RFC822.SIZE and UID. Since RFC822 is just RFC822.HEADER and RFC822.TEXT combined, we have one less variable to worry about.
One thing I haven’t really figured out yet is how to deal with the message number. Since we cannot assume that you will receive the message in the same sequence between queries, we probably need to always sort the list by message_id and then just give each message a number based on this list.
Let’s start with UID, as this is the easiest one. You’d issue this command as follows:
FETCH 1:6 (UID)
The expected result would look something like this:
1 FETCH (UID 1029955483)
2 FETCH (UID 1029955484)
3 FETCH (UID 1029955485)
4 FETCH (UID 1029955486)
5 FETCH (UID 1029955487)
6 FETCH (UID 1029955488)
The equivalent command in Facebook’s API would be something like this (I’m not sure if it’s possible to combine queries like this, but you get the idea):
SELECT message_id FROM message WHERE thread_id IN (SELECT thread_id FROM thread WHERE folder_id = 0)
You would then use that output as the UID.
Flags are pretty simple too (since we don’t care much about the actual result). You’d issue the command as follows:
FETCH 1:2 (flags)
And the result would look like this:
* 1 FETCH (FLAGS (\Seen))
* 2 FETCH (FLAGS (\Seen))
As I mentioned above, we don’t really care much for the result here (as it’s not implemented on Facebook’s side), all we need to know is the number of messages in total (so that you cannot issue FETCH 1:999999999999 and get 999999999999 entries back if there are only 15 messages available). Since we already know how to get the message count, we simply use that and return ‘FLAGS (\Seen)’ to each message.
I suppose you could implement the \Seen flag properly as described in RFC 3501 in the IMAP server itself, but that is beyond the scope of this article.
The same goes for RFC822.SIZE as for FLAGS. Since we do not know the message size, the best thing we can do is to just define it to be an arbitrary (or perhaps a random) value.
You’d issue the FETCH RFC822.SIZE as follows:
FETCH 1:6 (RFC822.SIZE)
The expected result would look something like this:
1 FETCH (RFC822.SIZE 2401)
2 FETCH (RFC822.SIZE 2935)
3 FETCH (RFC822.SIZE 2214)
4 FETCH (RFC822.SIZE 2165)
5 FETCH (RFC822.SIZE 551351)
6 FETCH (RFC822.SIZE 1359)
RFC822.TEXT is also fairly easy. It’s simply the content of the message. The result from FETCH RFC822.TEXT command would look something like this:
FETCH 1 (RFC822.TEXT)
Where the server would respond with:
This is the body of the message
The Facebook equivalent would be:
SELECT body FROM message WHERE message_id=[the message_id the message number 1]
Ok, this is the tricky part. Now we need pull out quite a bit of data from Facebook and arrange it according to the IMAP RFC.
Here’s an example of how it would look in IMAP:
FETCH 1 (RFC822.HEADER)
The server would then return something like this for a message with a very minimalistic header:
Date: 27 Aug 76 0932 PDT
From: Ken Davis
Subject: Re: The Syntax in the RFC
To: George Jones
(Copied from RFC822)
With that in mind, all we need to do is to look up those values in Facebook’s API and rewrite them to match the example above.
From the Message API, we need to pull out the author_id and created_time. Since we’d like to know the name (not just the id of the person who sent the message, we also need to look up the name of the author_id.
Without going into the exact queries, the message header would look something like this:
Date: [created_at from the Message API]
From: [The real name from author_id in Message API] <[author_id from the Message API]@facebookmail.com>
Subject: [subject from Thread API]
Reply-To : email@example.com
To: [real name from recipients in Thread API <[recipients from Threads API]@facebookmail.com>
Message-ID: <[message_id from Message API]@facebookmail.com>
That should be enough to make the header of the message compatible with the RFCs.
You can get the ‘status’ of a folder in IMAP without having to open it with the STATUS command. There are four variables that can be used with the STATUS command, namely: MESSAGES, RECENT, UIDNEXT and UIDVALIDITY.
The most relevant variable here is the MESSAGES variable, as it gives a message count on the folder. You’d issue the command as follows:
STATUS Inbox (MESSAGES)
The return would look something like:
STATUS Inbox (MESSAGES 2)
This command can be replicated fairly simple with Facebook’s API. We could issue the command:
SELECT message_count FROM thread WHERE folder_id = 0
We would then use the result and replace the ’2′ in the example result above.
Deleting a message is not possible as far as I can tell.
Marking message as read/unread
Not possible either from what I can tell.
Given that Facebook’s API is query based, search is one of the areas where it really excels. However, as the SEARCH command in IMAP is pretty complex, I’m not going to cover that. But just to give you an idea how search works in Facebook’s API, this is an example:
SELECT ... FROM thread WHERE CONTAINS('hello')
It’s not necessary to implement any support for logging out in the Facebook side. We do however, need to implement this in the IMAP server. It’s very straight forward. When the client issues
The remote server closes the connection. Simple as pie.
As I mentioned in the introduction, the intention of this guide is not to create a step-by-step guide on how to write an IMAP proxy for Facebook. It’s more a theoretical proof-of-concept that outlines fundamental features in IMAP and how they can be (with some work) replicated in Facebook’s API.
That said, I’d be happy to see if someone took this guide and continued to work on it and created a final product. I’d be even more happy if the person Open Sourced it.