1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public
5
* License Version 1.1 (the "MPL"); you may not use this file
6
* except in compliance with the MPL. You may obtain a copy of
7
* the MPL at http://www.mozilla.org/MPL/
9
* Software distributed under the MPL is distributed on an "AS
10
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11
* implied. See the MPL for the specific language governing
12
* rights and limitations under the MPL.
14
* The Original Code is Enigmail.
16
* The Initial Developer of the Original Code is Ramalingam Saravanan.
17
* Portions created by Ramalingam Saravanan <sarava@sarava.net> are
18
* Copyright (C) 2002 Ramalingam Saravanan. All Rights Reserved.
21
* Patrick Brunschwig <patrick@mozilla-enigmail.org>
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
34
* ***** END LICENSE BLOCK ***** */
36
// Logging of debug output
37
// The following define statement should occur before any include statements
38
#define FORCE_PR_LOG /* Allow logging even in release build */
43
#include "nsIInputStream.h"
44
#include "nsIThread.h"
45
#include "nsStringAPI.h"
46
#include "nsNetUtil.h"
47
#include "mimehdrs2.h"
48
#include "nsMimeTypes.h"
49
#include "nsMailHeaders.h"
51
#include "nsEnigMimeListener.h"
54
PRLogModuleInfo* gEnigMimeListenerLog = NULL;
57
#define ERROR_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_ERROR,args)
58
#define WARNING_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_WARNING,args)
59
#define DEBUG_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_DEBUG,args)
61
#define NS_PIPE_CONSOLE_BUFFER_SIZE (1024)
63
static const PRUint32 kCharMax = 1024;
65
#define MK_MIME_ERROR_WRITING_FILE -1
67
///////////////////////////////////////////////////////////////////////////////
69
// nsEnigMimeListener implementation
71
// nsISupports implementation
72
NS_IMPL_THREADSAFE_ISUPPORTS4(nsEnigMimeListener,
79
// nsEnigMimeListener implementation
80
nsEnigMimeListener::nsEnigMimeListener()
81
: mInitialized(PR_FALSE),
82
mRequestStarted(PR_FALSE),
83
mSkipHeaders(PR_FALSE),
93
mContentDisposition(""),
96
mDecodeContent(PR_FALSE),
102
mHeaderSearchCounter(0),
104
mHeadersFinalCR(PR_FALSE),
105
mHeadersLinebreak(2),
113
mSubPartTreatment(PR_FALSE),
121
if (gEnigMimeListenerLog == NULL) {
122
gEnigMimeListenerLog = PR_NewLogModule("nsEnigMimeListener");
128
nsCOMPtr<nsIThread> myThread;
129
rv = ENIG_GET_THREAD(myThread);
130
DEBUG_LOG(("nsEnigMimeListener:: <<<<<<<<< CTOR(%p): myThread=%p\n",
131
this, myThread.get()));
136
nsEnigMimeListener::~nsEnigMimeListener()
140
nsCOMPtr<nsIThread> myThread;
141
rv = ENIG_GET_THREAD(myThread);
142
DEBUG_LOG(("nsEnigMimeListener:: >>>>>>>>> DTOR(%p): myThread=%p\n",
143
this, myThread.get()));
147
// Clear decoder buffer
148
MimeDecoderDestroy(mDecoderData, PR_FALSE);
152
// Release owning refs
158
///////////////////////////////////////////////////////////////////////////////
159
// nsIEnigMimeListener methods
160
///////////////////////////////////////////////////////////////////////////////
163
nsEnigMimeListener::Init(nsIStreamListener* listener, nsISupports* ctxt,
164
PRUint32 maxHeaderBytes, EMBool skipHeaders,
165
EMBool skipBody, EMBool decodeContent)
167
DEBUG_LOG(("nsEnigMimeListener::Init: (%p) %d, %d, %d, %d\n", this,
168
maxHeaderBytes, skipHeaders, skipBody, decodeContent));
171
return NS_ERROR_NULL_POINTER;
174
return NS_ERROR_NOT_IMPLEMENTED;
176
mListener = listener;
179
mMaxHeaderBytes = maxHeaderBytes;
181
mSkipHeaders = skipHeaders;
182
mSkipBody = skipBody;
183
mDecodeContent = decodeContent;
185
// There is implicitly a newline preceding the first character
186
mHeadersLinebreak = 2;
187
mHeadersFinalCR = PR_FALSE;
189
mInitialized = PR_TRUE;
196
nsEnigMimeListener::Write(const char* buf, PRUint32 count,
197
nsIRequest* aRequest, nsISupports* aContext)
201
DEBUG_LOG(("nsEnigMimeListener::Write: (%p) %d\n", this, count));
204
return Transmit(buf, count, aRequest, aContext);
206
// Search for headers
207
EMBool startingRequest = HeaderSearch(buf, count);
208
if (!startingRequest)
211
rv = StartRequest(aRequest, aContext);
219
EnigMimeListener_write(const char *buf, PRInt32 size, void *closure)
221
DEBUG_LOG(("nsEnigMimeListener::EnigMimeListener_write: (%p) %d\n", closure, size));
224
return NS_ERROR_FAILURE;
226
nsEnigMimeListener* enigMimeListener = (nsEnigMimeListener *) closure;
228
return enigMimeListener->SendStream(buf, size, NULL, NULL);
233
nsEnigMimeListener::Transmit(const char* buf, PRUint32 count,
234
nsIRequest* aRequest, nsISupports* aContext)
236
DEBUG_LOG(("nsEnigMimeListener::Transmit: (%p) %d\n", this, count));
239
return SendStream(buf, count, aRequest, aContext);
242
// Decode data before transmitting to listener
243
int status = MimeDecoderWrite(mDecoderData, buf, count);
245
return (status == 0) ? NS_OK : NS_ERROR_FAILURE;
250
nsEnigMimeListener::SendStream(const char* buf, PRUint32 count,
251
nsIRequest* aRequest, nsISupports* aContext)
255
DEBUG_LOG(("nsEnigMimeListener::SendStream: (%p) %d\n", this, count));
260
// Transmit data to listener
263
mStreamLength = count;
265
rv = mListener->OnDataAvailable(aRequest,
266
mContext ? mContext.get() : aContext,
267
(nsIInputStream*)(this),
276
nsEnigMimeListener::GetHeaders(nsACString &aHeaders)
279
DEBUG_LOG(("nsEnigMimeListener::GetHeaders: %d\n", mHeaders.Length()));
284
nsEnigMimeListener::GetLinebreak(nsACString &aLinebreak)
286
aLinebreak = mLinebreak;
287
DEBUG_LOG(("nsEnigMimeListener::GetLinebreak: %d\n", mLinebreak.Length()));
292
nsEnigMimeListener::GetContentType(nsACString &aContentType)
294
aContentType = mContentType;
295
DEBUG_LOG(("nsEnigMimeListener::GetContentType: %s\n", mContentType.get()));
300
nsEnigMimeListener::GetContentCharset(nsACString &aContentCharset)
302
aContentCharset = mContentCharset;
303
DEBUG_LOG(("nsEnigMimeListener::GetContentCharset: %s\n", mContentCharset.get()));
308
nsEnigMimeListener::GetContentBoundary(nsACString &aContentBoundary)
310
aContentBoundary = mContentBoundary;
311
DEBUG_LOG(("nsEnigMimeListener::GetContentBoundary: %s\n", mContentBoundary.get()));
316
nsEnigMimeListener::GetContentProtocol(nsACString &aContentProtocol)
318
aContentProtocol = mContentProtocol;
319
DEBUG_LOG(("nsEnigMimeListener::GetContentProtocol: %s\n", mContentProtocol.get()));
324
nsEnigMimeListener::GetContentMicalg(nsACString &aContentMicalg)
326
aContentMicalg = mContentMicalg;
327
DEBUG_LOG(("nsEnigMimeListener::GetContentMicalg: %s\n", mContentMicalg.get()));
332
nsEnigMimeListener::GetContentEncoding(nsACString &aContentEncoding)
334
aContentEncoding = mContentEncoding;
335
DEBUG_LOG(("nsEnigMimeListener::GetContentEncoding: %s\n", mContentEncoding.get()));
340
nsEnigMimeListener::GetContentDisposition(nsACString &aContentDisposition)
342
aContentDisposition = mContentDisposition;
343
DEBUG_LOG(("nsEnigMimeListener::GetContentDisposition: %s\n", mContentDisposition.get()));
348
nsEnigMimeListener::GetContentLength(PRInt32 *aContentLength)
350
DEBUG_LOG(("nsEnigMimeListener::GetContentLength: \n"));
351
*aContentLength = mContentLength;
355
///////////////////////////////////////////////////////////////////////////////
356
// nsIRequestObserver methods
357
///////////////////////////////////////////////////////////////////////////////
360
nsEnigMimeListener::OnStartRequest(nsIRequest *aRequest,
361
nsISupports *aContext)
363
DEBUG_LOG(("nsEnigMimeListener::OnStartRequest: (%p)\n", this));
366
return NS_ERROR_NOT_INITIALIZED;
372
nsEnigMimeListener::OnStopRequest(nsIRequest* aRequest,
373
nsISupports* aContext,
378
DEBUG_LOG(("nsEnigMimeListener::OnStopRequest: (%p)\n", this));
380
// Ensure that OnStopRequest call chain does not break by failing softly
382
if (!mRequestStarted) {
384
if (mHeadersFinalCR) {
385
// Handle special case of terminating CR with no content
386
mHeadersFinalCR = PR_FALSE;
397
rv = StartRequest(aRequest, aContext);
399
aStatus = NS_BINDING_ABORTED;
403
// Clear decoder buffer
404
MimeDecoderDestroy(mDecoderData, PR_FALSE);
409
rv = mListener->OnStopRequest(aRequest,
410
mContext ? mContext.get() : aContext,
413
aStatus = NS_BINDING_ABORTED;
416
// Release owning refs
420
return (aStatus == NS_BINDING_ABORTED) ? NS_ERROR_FAILURE : NS_OK;
423
///////////////////////////////////////////////////////////////////////////////
424
// nsIStreamListener method
425
///////////////////////////////////////////////////////////////////////////////
428
nsEnigMimeListener::OnDataAvailable(nsIRequest* aRequest,
429
nsISupports* aContext,
430
nsIInputStream *aInputStream,
431
#if MOZILLA_MAJOR_VERSION < 18
432
PRUint32 aSourceOffset,
434
PRUint64 aSourceOffset,
440
DEBUG_LOG(("nsEnigMimeListener::OnDataAvailable: (%p) %d\n", this, aLength));
443
return NS_ERROR_NOT_INITIALIZED;
446
PRUint32 readCount, readMax;
448
while ((aLength > 0) && (!mRequestStarted || mDecoderData) ) {
449
// Searching for headers or decoding content
451
readMax = (aLength < kCharMax) ? aLength : kCharMax;
452
rv = aInputStream->Read((char *) buf, readMax, &readCount);
454
ERROR_LOG(("nsEnigMimeListener::OnDataAvailable: Error in reading from input stream, %x\n", rv));
461
aLength -= readCount;
462
aSourceOffset += readCount;
464
rv = Write(buf, readCount, aRequest, aContext);
469
// Not searching for headers and not decoding content
470
if (!mSkipBody && (aLength > 0) && mListener) {
471
// Transmit body data unread
472
rv = mListener->OnDataAvailable(aRequest,
473
mContext ? mContext.get() : aContext,
474
aInputStream, mDataOffset, aLength);
475
mDataOffset += aLength;
486
nsEnigMimeListener::StartRequest(nsIRequest* aRequest, nsISupports* aContext)
490
DEBUG_LOG(("nsEnigMimeListener::StartRequest: (%p)\n", this));
492
if (!mHeaders.IsEmpty()) {
493
// Try to parse headers
494
ParseMimeHeaders(mHeaders.get(), mHeaders.Length());
498
rv = mListener->OnStartRequest(aRequest,
499
mContext ? mContext.get() : aContext);
504
mRequestStarted = PR_TRUE;
506
if (mHeaders.IsEmpty() && mSkipBody) {
507
// No headers terminated and skipping body; so discard whatever we have
511
if (!mDataStr.IsEmpty()) {
512
// Transmit header/body data already in buffer
513
nsCAutoString temStr( mDataStr );
515
mDataOffset += mDataStr.Length();
518
rv = Transmit(temStr.get(), temStr.Length(), aRequest, aContext);
528
nsEnigMimeListener::HeaderSearch(const char* buf, PRUint32 count)
530
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: (%p) count=%d\n", this, count));
532
mHeaderSearchCounter++;
534
if (mMaxHeaderBytes <= 0) {
535
// Not looking for MIME headers; start request immediately
542
PRUint32 bytesAvailable = mMaxHeaderBytes - mDataStr.Length();
543
NS_ASSERTION(bytesAvailable > 0, "bytesAvailable <= 0");
545
EMBool lastSegment = (bytesAvailable <= count);
547
PRUint32 scanLen = lastSegment ? bytesAvailable : count;
549
EMBool headersFound = PR_FALSE;
551
PRUint32 startOffset = 0;
554
if (mSubPartTreatment) {
556
// this is a HACK necessary because Mozilla does not deliver
557
// a subpart starting with its headers (so we get the
558
// part on a higher level and sort out things manually!)
559
// there is (so far) no way to get the headers of an
560
// arbitrary message mime part
561
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: subparts treatment\n"));
564
if (((ch=='\n') || (ch=='\r')) &&
571
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: startOffset=%d\n",startOffset));
578
// set j=startOffset needed if startOffset == 0!
581
// Solution for how to do it, if the content-type info
582
// would be available
583
nsCAutoString cType("Content-Type: multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=\"J2SCkAp4GZ/dPZZf\"\n\n");
584
mDataStr.Append(cType.get(), cType.Length());
589
mDataStr.Append(buf, count);
591
mHeadersLinebreak = 0;
594
mSubPartTreatment = PR_FALSE;
601
if (mHeadersFinalCR) {
602
// End-of-headers found
603
mHeadersFinalCR = PR_FALSE;
608
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CRLF"));
613
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CR"));
616
headersFound = PR_TRUE;
623
if (mHeadersLinebreak == 2) {
624
// End-of-headers found
625
headersFound = PR_TRUE;
629
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final LF"));
633
mHeadersLinebreak = 2;
635
} else if (ch == '\r') {
637
if (mHeadersLinebreak > 0) {
639
mHeadersFinalCR = PR_TRUE;
641
mHeadersLinebreak = 1;
645
mHeadersLinebreak = 0;
651
DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: offset=%d\n", offset));
654
// Copy headers out of stream buffer
656
mDataStr.Append(buf+startOffset, offset-startOffset);
665
if (!mSkipBody && (offset < count)) {
666
// Copy remaining data into stream buffer
667
mDataStr.Append(buf+offset, count-offset);
670
} else if (!lastSegment) {
672
mDataStr.Append(buf, count);
675
return headersFound || lastSegment;
679
__ReplaceCSubstring (nsACString &string, const char* replace, const char* with)
681
PRInt32 i = string.Find (replace);
683
string.Replace (i, strlen (replace), with);
684
i = string.Find (replace);
689
__ReplaceCChar (nsACString &string, const char replace, const char with)
691
PRInt32 i = string.FindChar (replace);
693
string.Replace (i, 1, (const char*) &with, 1);
694
i = string.FindChar (replace);
699
nsEnigMimeListener::ParseMimeHeaders(const char* mimeHeaders, PRUint32 count)
701
DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders, count=%d\n", count));
703
// Copy headers string
704
nsCAutoString headers(mimeHeaders, count);
706
// Replace CRLF with just LF
707
__ReplaceCSubstring(headers, "\r\n", "\n");
709
// Replace CR with LF (for MAC-style line endings)
710
__ReplaceCChar(headers, '\r', '\n');
712
// Eliminate all leading whitespace (including linefeeds)
713
headers.Trim(" \t\n", PR_TRUE, PR_FALSE);
715
if (headers.Length() <= 3) {
716
// No headers to parse
720
// Handle continuation of MIME headers, i.e., newline followed by whitespace
721
__ReplaceCSubstring(headers, "\n ", " ");
722
__ReplaceCSubstring(headers, "\n\t", "\t");
724
//DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders: headers='%s'\n", headers.get()));
727
while (offset < headers.Length()) {
728
PRInt32 lineEnd = headers.FindChar('\n', offset);
731
// Header line terminator not found
732
NS_NOTREACHED("lineEnd == kNotFound");
736
// Normal exit if empty header line
737
if (lineEnd == (int)offset)
741
ParseHeader((headers.get())+offset, lineEnd - offset);
750
nsEnigMimeListener::ParseHeader(const char* header, PRUint32 count)
753
//DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", header));
755
if (!header || (count <= 0) )
758
// Create header string
759
nsCAutoString headerStr(header, count);
761
//DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", headerStr.get()));
763
colonOffset = headerStr.FindChar(':');
767
// Null header key not allowed
768
if (colonOffset == 0)
771
// Extract header key (not case-sensitive)
772
nsCAutoString headerKey = (nsCString) nsDependentCSubstring (headerStr, 0, colonOffset);
773
ToLowerCase(headerKey);
776
// Extract header value, trimming leading/trailing whitespace
777
nsCAutoString buf = (nsCString) nsDependentCSubstring (headerStr, colonOffset+1, headerStr.Length() - colonOffset);
778
buf.Trim(" ", PR_TRUE, PR_TRUE);
780
//DEBUG_LOG(("nsEnigMimeListener::ParseHeader: '%s': %s\n", headerKey.get(), buf.get()));
782
PRInt32 semicolonOffset = buf.FindChar(';');
784
nsCString headerValue;
785
if (semicolonOffset < 0) {
787
headerValue = ((nsCString)buf).get();
790
// Extract value to left of parameters
791
headerValue = nsDependentCSubstring (buf, 0, semicolonOffset);
794
// Trim leading and trailing spaces in header value
795
headerValue.Trim(" ", PR_TRUE, PR_TRUE);
797
if (headerKey.Equals("content-type")) {
798
mContentType = headerValue;
800
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentType=%s\n",
801
mContentType.get()));
803
if (!buf.IsEmpty()) {
804
char *charset = MimeHeaders_get_parameter(buf.get(),
805
HEADER_PARM_CHARSET, NULL, NULL);
806
char *boundary = MimeHeaders_get_parameter(buf.get(),
807
HEADER_PARM_BOUNDARY, NULL, NULL);
808
char *protocol = MimeHeaders_get_parameter(buf.get(),
809
PARAM_PROTOCOL, NULL, NULL);
810
char *micalg = MimeHeaders_get_parameter(buf.get(),
811
PARAM_MICALG, NULL, NULL);
814
mContentCharset = charset;
817
mContentBoundary = boundary;
820
mContentProtocol = protocol;
823
mContentMicalg = micalg;
830
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentCharset=%s\n",
831
mContentCharset.get()));
833
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentBoundary=%s\n",
834
mContentBoundary.get()));
836
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentProtocol=%s\n",
837
mContentProtocol.get()));
839
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentMicalg=%s\n",
840
mContentMicalg.get()));
843
} else if (headerKey.Equals("content-transfer-encoding")) {
844
mContentEncoding = buf;
845
ToLowerCase(mContentEncoding);
847
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentEncoding=%s\n",
848
mContentEncoding.get()));
850
} else if (headerKey.Equals("content-disposition")) {
851
mContentDisposition = buf;
853
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentDisposition=%s\n",
854
mContentDisposition.get()));
856
} else if (headerKey.Equals("content-length")) {
858
PRInt32 value = headerValue.ToInteger(&status);
860
if (NS_SUCCEEDED(status))
861
mContentLength = value;
863
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContenLengtht=%d\n",
871
///////////////////////////////////////////////////////////////////////////////
872
// nsIInputStream methods
873
///////////////////////////////////////////////////////////////////////////////
876
#if MOZILLA_MAJOR_VERSION < 17
877
nsEnigMimeListener::Available(PRUint32* _retval)
879
nsEnigMimeListener::Available(PRUint64* _retval)
883
return NS_ERROR_NULL_POINTER;
885
*_retval = (mStreamLength > mStreamOffset) ?
886
mStreamLength - mStreamOffset : 0;
888
DEBUG_LOG(("nsEnigMimeListener::Available: (%p) %d\n", this, *_retval));
894
nsEnigMimeListener::Read(char* buf, PRUint32 count,
897
DEBUG_LOG(("nsEnigMimeListener::Read: (%p) %d\n", this, count));
899
if (!buf || !readCount)
900
return NS_ERROR_NULL_POINTER;
902
PRInt32 avail = (mStreamLength > mStreamOffset) ?
903
mStreamLength - mStreamOffset : 0;
905
*readCount = ((PRUint32) avail > count) ? count : avail;
908
memcpy(buf, mStreamBuf+mStreamOffset, *readCount);
909
mStreamOffset += *readCount;
912
if (mStreamOffset >= mStreamLength) {
920
nsEnigMimeListener::ReadSegments(nsWriteSegmentFun writer,
921
void * aClosure, PRUint32 count,
924
DEBUG_LOG(("nsEnigMimeListener::ReadSegments: %d\n", count));
927
return NS_ERROR_NULL_POINTER;
929
PRInt32 avail = (mStreamLength > mStreamOffset) ?
930
mStreamLength - mStreamOffset : 0;
932
PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
938
nsresult rv = writer((nsIInputStream*)(this),
939
aClosure, mStreamBuf+mStreamOffset,
940
mStreamOffset, readyCount, readCount);
944
mStreamOffset += *readCount;
947
if (mStreamOffset >= mStreamLength) {
955
nsEnigMimeListener::IsNonBlocking(EMBool *aNonBlocking)
957
DEBUG_LOG(("nsEnigMimeListener::IsNonBlocking: \n"));
959
*aNonBlocking = PR_TRUE;
964
nsEnigMimeListener::GetSubPartTreatment(EMBool* aSubPartTreatment)
966
*aSubPartTreatment = mSubPartTreatment;
971
nsEnigMimeListener::SetSubPartTreatment(EMBool aSubPartTreatment)
973
DEBUG_LOG(("nsEnigMimeListener::SetSubPartTreatment: %d\n", aSubPartTreatment));
975
mSubPartTreatment = aSubPartTreatment;
980
nsEnigMimeListener::Close()
982
DEBUG_LOG(("nsEnigMimeListener::Close: (%p)\n", this));