I've been looking at the latest version of the MIME
parser (0.9.7 source drop) and thought someone may be able
to shed some light on design issues of the MIME core.
My goal:
+ Parse incoming RFC 822 messages that
are S/MIME encapsulated.
+ Obtain the message body, attachments, their
content types, and obtain security attributes
(signed, encrypted, validity).
+ Make it stand-alone with no user-interface.
Now, I've seen two methods for using the MIME engine:
1. Use the Stream Converter service to
"re-write" an RFC 822 message.
2. Call the lower-level MIME parsing interface
directly.
The default converters take message/rfc822 and convert
them into HTML (text/html), etc. I'm more interested
in a MIME parse tree so I can extract information about
the structure of the MIME message.
So, I'm looking more closely at option [2]. Here's
what I'm doing. Following the example in mimedrft.cpp,
I am creating a new RFC822 MIME object and parsing my
input file (stream). Here's some pseudo code basically
taken right out of mimedrft.cpp:
...
mdd->options->decompose_file_p = PR_TRUE;
...
object = mime_new(
(MimeObjectClass *) &mimeMessageClass,
(MimeHeaders *) NULL,
MESSAGE_RFC822);
obj->clazz->initialize();
obj->clazz->parse_begin();
while (input data)
{
obj->class->parse_buffer();
}
obj->clazz->parse_eof();
Okay, simple enough, and it works in some cases.
The following are the problems/issues I'm having with S/MIME
related messages. Hopefully they are not operator error.
Problem #1
----------
Clear-text signed messages don't get their crypto_msg_signed_p
member set. After the parse is complete I make a call to
mime_get_crypt_state(mdd->obj, ...).
The crypt_stamped_p, crypto_msg_signed_p and crypto_msg_encrypted_p
members are all 0, even though the original message was a clear-text
signed S/MIME message.
A call to mime_find_security_info_of_part("0", obj, ...) returns
security information. That is, the container object is indeed
mimeMultipartSignedCMSClass and it has a valid crypto_closure.
I traced this down to the fact that mime_set_crypto_stamp()
never gets call when parsing a clear-text signed message using
the above snippet.
Setting crypto state seems [incorrectly] all wrapped up in
re-writing (as HTML). Of course I don't want to use the MIME
engine for re-writing as HTML.
Problem #2
----------
Opaque-signed messages cause a crash. If the incoming RFC 822
message is not a clear-text signed message, and decompose_file_p
is set to PR_TRUE, the MIME parser crashes.
The problem is that when the parser sees the Content-type:
of application/x-pkcs7-mime;smime-type=signed-data, it starts
processing the base64 message body. When it's done processing
the base64 content, it tries to emit the buffered child
MimeEncrypted_emity_buffered_child(), but there are no
headers passed into the mime_decompose_file_init_fn()
(from mimedrft.cpp), probably since the inner content of
the base64 message hadn't been parsed yet. An assertion
fires here. If I continue then I see the crash since an
output file stream object has not been initialized.
I've attached the call stack to the end of this message.
So, are there any special set-up tricks that I missed in
the above code snippet? That is, configuring the MIME
parser? Maybe I'm missing an important mdd->option value
initialization?
Problem #3
----------
The core of the MIME engine makes some assumptions about
it's containing environment.
When I went looking through the core of the MIME engine to
figure out Problem #1, I noticed that security information
gets communicated outside the MIME parser core using some
event firing mechanism. Specifically, in mimemcms.cpp, the
function MimeMultCMS_generate(), I was rather surprised to
see:
nsIChannel *channel = msd->channel; // note the lack of ref counting...
if (channel)
{
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIMsgWindow> msgWindow;
nsCOMPtr<nsIMsgHeaderSink> headerSink;
nsCOMPtr<nsIMsgMailNewsUrl> msgurl;
nsCOMPtr<nsISupports> securityInfo;
nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
...
}
Does my containing code really need an nsIMsgWindow and an
nsIMsgMailNewsUrl to obtain information about the security
status of a MIME message?
First, I don't have any implementations of an nsIMsgWindow
since I don't really have a graphical user-interface, and
second I'm not using an nsIMsgMailNewsUrl as the file I'm
parsing was referenced using file://.
Two comments here:
1. It seems like a mistake for the core of
the MIME engine to make assumptions about
the presence of Windows and MailNewsUrls, no?
Isn't there a more generic event firing
(or listening) interface that the MIME
engine could use? (Or should use).
2. Uh, I forgot the second comment by the time
I typed all this text.... :-)
Cheers,
Daniel
======================= CUT HERE ==============================
Problem #2 Call Stack
=====================
Assertion Failure
-----------------
nsDebug::Assertion(const char * 0x0048590c, const char * 0x004858fc,
const char * 0x004858bc, int 0x00000677) line 290 + 13 bytes
mime_decompose_file_init_fn(void * 0x02095c70, MimeHeaders * 0x00000000)
line 1655 + 38 bytes
MimeEncrypted_emit_buffered_child(MimeObject * 0x02098978) line 503 + 29
bytes
MimeEncrypted_parse_eof(MimeObject * 0x02098978, int 0x00000000) line
252 + 9 bytes
MimeContainer_parse_eof(MimeObject * 0x02095e20, int 0x00000000) line
141 + 16 bytes
MimeMessage_parse_eof(MimeObject * 0x02095e20, int 0x00000000) line 518
+ 14 bytes
TestSMIMEMessage() line 389 + 18 bytes
Crash after assertion continuation
----------------------------------
nsCOMPtr<nsIOutputStream>::nsCOMPtr<nsIOutputStream>(const
nsCOMPtr<nsIOutputStream> & {...}) line 525 + 10 bytes
nsOutputStream::GetIStream() line 262 + 15 bytes
mime_decompose_file_close_fn(void * 0x02095c70) line 1952 + 19 bytes
MimeMessage_parse_eof(MimeObject * 0x02095e20, int 0x00000000) line 563
+ 22 bytes
TestSMIMEMessage() line 389 + 18 bytes