The Deep End

go to! email!

Thursday, February 24, 2005

Thunderbird Extensions: How to Get the Body of A Message

The XPCOM component nsIMsgHdr is, as you would expect, the header of a message. Oddly there doesn't seem to be a component like "nsITheMsg" that includes the header--the header is, for all practical purposes, the message. This means that getting to the body of the message is really frustrating. So--to get the body:

First you'll need to create an nsIInputStream . Because the stream that you create will need to "contain" the message, the getOfflineFileStream method of nsIMsgFolder will return a relevant stream.

The parameters of getOfflineFileStream are (1) the message key, which is a property of the header (yourHdr.messageKey); and two so-called "out" parameters--which is basically the way that javascript returns more than one thing. All that you need to do with these to make Thunderbird happy is to initialize them as Objects and then just pass them in. Like so:
var offset = new Object();
var messageSize = new Object();


is = fdr.getOfflineFileStream(hdr.messageKey,offset,messageSize);

alert("message: "+e.message);

fdr is the folder. If you're having trouble getting to the right folder/any folder, see the subsequent post on navigating folders with XPCOM.
Then, because nsIInputStream is a "frozen interface" (I'm not entirely sure what a frozen interface is, so I'm not going to guess) you'll need to use a safe, friendly wrapper class called nsIScriptableInputStream.
This code will get you the body and headers of the message (in other words the entire message):

var factory = Components.classes[";1"];

var sis = factory.createInstance(nsIScriptableInputStream);

bodyAndHdr =;

//the -10 gets rid of this really weird "From - Tue " thing
// (which is an exact quote). The reason why it shows "From - Tue" is
//that it's actually part of the next message's header, and in fact,
//the stream contains all of the messages in the folder.
//it's as yet unclear why hdr.messageSize should be off by 10, or why
//the out parameter to is (which is also called messageSize) always
//shows up as 0.

alert("message: "+e.message);
Weirdly, offset (the out parameter "returned" from getOfflineFileStream) has the same value as hdr.messageSize. This may be a problem with the API, because offset and messageSize are entirely different things.

As a side note, if you want to do anything at all with offset, use offset.value because offset is an object.

The last part to successfully getting the body--which is really sort of optional--is to open the body in a compose window.

One easy way to separate the hdr from the body in bodyAndHdr is:
var hdrstr = bodyAndHdr.indexOf("\r\n\r\n"); //marks the end of the //headers
body = bodyAndHdr.substring(hdrstr+1,body.length);


  • At Tuesday, April 19, 2005 4:30:00 PM, Anonymous Ted Mielczarek said…

    FYI, "frozen" just means the interface won't change in the future. Unfrozen interfaces aren't really safe to use as they aren't guaranteed to stay the same in the next Mozilla release. Using a scriptable input stream has nothing to do with it being frozen, just that a normal input stream isn't friendly to use from javascript.

  • At Monday, December 05, 2005 8:29:00 AM, Anonymous Anonymous said…

    Thank's for this information. I used it in my extension Yamb (

    However, I ran into a few problems for which I found no (or no good solution):

    1/ We get the whole raw body. That means, including encoded attachments, multiparts messages (plain, html), and embedded forwarded messages. It may be difficult to get the text part in it.

    2/ After parsing this, I get the text part, but this is still encoded, in different ways depending on the sender email client (for accented or complex chars). I don't know how to properly decode this.

    3/ Bigest problem: I use this to display a popup (mail biff) when a new message arrives. If the message is moved to an other folder using a filter, getOfflineFileStream throws an exception. I suppose the mail file in which the body is retrieved has not yet been updated, whereas the message header already contains the new folder. (The popup is triggered by a FolderListener).

    If you have any ideas about these ppoints, I'll be glad you to share them on my site forum or guestbook.


  • At Friday, May 12, 2006 10:16:00 AM, Anonymous Anonymous said…

    Thanks for your post. This has been very helpful. It works great with offline messages.

    Is there a way to get the body of messages on an imap server or new messages from a pop server? Currently when I try the method you show with those 2 types of messages it fails. For example, when I try it with a message in an imap account I get the following response:

    "Component returned failure code: 0x80520012 (NS_ERROR_FILE_NOT_FOUND) {nsIMsgFolder.getOfflineFileStream]

    Any help would be greatly appreciated.



Post a Comment

<< Home