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 License
5
* Version 1.1 (the "MPL"); you may not use this file except in
6
* compliance with the MPL. You may obtain a copy of the MPL at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the MPL is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
11
* for the specific language governing rights and limitations under the
14
* The Original Code is Enigmail.
16
* The Initial Developer of the Original Code is
17
* Ramalingam Saravanan <sarava@sarava.net>
18
* Portions created by the Initial Developer are Copyright (C) 2002
19
* the Initial Developer. All Rights Reserved.
22
* Patrick Brunschwig <patrick.brunschwig@gmx.net>
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the MPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
38
// Logging of debug output
39
// The following define statement should occur before any include statements
40
#define FORCE_PR_LOG /* Allow logging even in release build */
45
#include "nsAutoLock.h"
46
#include "nsIInputStream.h"
47
#include "nsIThread.h"
49
#include "nsNetUtil.h"
50
#include "mimehdrs2.h"
51
#include "nsMimeTypes.h"
52
#include "nsMailHeaders.h"
53
#ifdef _ENIG_MOZILLA_1_8
54
#include "nsFileStream.h"
56
#undef MOZILLA_INTERNAL_API
58
#include "nsEnigMimeListener.h"
61
PRLogModuleInfo* gEnigMimeListenerLog = NULL;
64
#define ERROR_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_ERROR,args)
65
#define WARNING_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_WARNING,args)
66
#define DEBUG_LOG(args) PR_LOG(gEnigMimeListenerLog,PR_LOG_DEBUG,args)
68
#define NS_PIPE_CONSOLE_BUFFER_SIZE (1024)
70
static const PRUint32 kCharMax = 1024;
72
#define MK_MIME_ERROR_WRITING_FILE -1
74
///////////////////////////////////////////////////////////////////////////////
76
// nsEnigMimeListener implementation
78
// nsISupports implementation
79
NS_IMPL_THREADSAFE_ISUPPORTS4(nsEnigMimeListener,
86
// nsEnigMimeListener implementation
87
nsEnigMimeListener::nsEnigMimeListener()
88
: mInitialized(PR_FALSE),
89
mRequestStarted(PR_FALSE),
90
mSkipHeaders(PR_FALSE),
100
mContentDisposition(""),
103
mDecodeContent(PR_FALSE),
104
mDecoderData(nsnull),
109
mHeaderSearchCounter(0),
111
mHeadersFinalCR(PR_FALSE),
112
mHeadersLinebreak(2),
120
mSubPartTreatment(PR_FALSE),
128
if (gEnigMimeListenerLog == nsnull) {
129
gEnigMimeListenerLog = PR_NewLogModule("nsEnigMimeListener");
135
nsCOMPtr<nsIThread> myThread;
136
rv = ENIG_GET_THREAD(myThread);
137
DEBUG_LOG(("nsEnigMimeListener:: <<<<<<<<< CTOR(%p): myThread=%p\n",
138
this, myThread.get()));
143
nsEnigMimeListener::~nsEnigMimeListener()
147
nsCOMPtr<nsIThread> myThread;
148
rv = ENIG_GET_THREAD(myThread);
149
DEBUG_LOG(("nsEnigMimeListener:: >>>>>>>>> DTOR(%p): myThread=%p\n",
150
this, myThread.get()));
154
// Clear decoder buffer
155
MimeDecoderDestroy(mDecoderData, PR_FALSE);
156
mDecoderData = nsnull;
159
// Release owning refs
165
///////////////////////////////////////////////////////////////////////////////
166
// nsIEnigMimeListener methods
167
///////////////////////////////////////////////////////////////////////////////
170
nsEnigMimeListener::Init(nsIStreamListener* listener, nsISupports* ctxt,
171
PRUint32 maxHeaderBytes, PRBool skipHeaders,
172
PRBool skipBody, PRBool decodeContent)
174
DEBUG_LOG(("nsEnigMimeListener::Init: (%p) %d, %d, %d, %d\n", this,
175
maxHeaderBytes, skipHeaders, skipBody, decodeContent));
178
return NS_ERROR_NULL_POINTER;
180
mListener = listener;
183
mMaxHeaderBytes = maxHeaderBytes;
185
mSkipHeaders = skipHeaders;
186
mSkipBody = skipBody;
187
mDecodeContent = decodeContent;
189
// There is implicitly a newline preceding the first character
190
mHeadersLinebreak = 2;
191
mHeadersFinalCR = PR_FALSE;
193
mInitialized = PR_TRUE;
200
nsEnigMimeListener::Write(const char* buf, PRUint32 count,
201
nsIRequest* aRequest, nsISupports* aContext)
205
DEBUG_LOG(("nsEnigMimeListener::Write: (%p) %d\n", this, count));
208
return Transmit(buf, count, aRequest, aContext);
210
// Search for headers
211
PRBool startingRequest = HeaderSearch(buf, count);
212
if (!startingRequest)
215
rv = StartRequest(aRequest, aContext);
223
EnigMimeListener_write(const char *buf, PRInt32 size, void *closure)
225
DEBUG_LOG(("nsEnigMimeListener::EnigMimeListener_write: (%p) %d\n", closure, size));
228
return NS_ERROR_FAILURE;
230
nsEnigMimeListener* enigMimeListener = (nsEnigMimeListener *) closure;
232
return enigMimeListener->SendStream(buf, size, nsnull, nsnull);
237
nsEnigMimeListener::Transmit(const char* buf, PRUint32 count,
238
nsIRequest* aRequest, nsISupports* aContext)
240
DEBUG_LOG(("nsEnigMimeListener::Transmit: (%p) %d\n", this, count));
243
return SendStream(buf, count, aRequest, aContext);
246
// Decode data before transmitting to listener
247
int status = MimeDecoderWrite(mDecoderData, buf, count);
249
return (status == 0) ? NS_OK : NS_ERROR_FAILURE;
254
nsEnigMimeListener::SendStream(const char* buf, PRUint32 count,
255
nsIRequest* aRequest, nsISupports* aContext)
259
DEBUG_LOG(("nsEnigMimeListener::SendStream: (%p) %d\n", this, count));
264
// Transmit data to listener
267
mStreamLength = count;
269
rv = mListener->OnDataAvailable(aRequest,
270
mContext ? mContext.get() : aContext,
271
(nsIInputStream*)(this),
280
nsEnigMimeListener::GetHeaders(nsACString &aHeaders)
283
DEBUG_LOG(("nsEnigMimeListener::GetHeaders: %d\n", mHeaders.Length()));
288
nsEnigMimeListener::GetLinebreak(nsACString &aLinebreak)
290
aLinebreak = mLinebreak;
291
DEBUG_LOG(("nsEnigMimeListener::GetLinebreak: %d\n", mLinebreak.Length()));
296
nsEnigMimeListener::GetContentType(nsACString &aContentType)
298
aContentType = mContentType;
299
DEBUG_LOG(("nsEnigMimeListener::GetContentType: %s\n", mContentType.get()));
304
nsEnigMimeListener::GetContentCharset(nsACString &aContentCharset)
306
aContentCharset = mContentCharset;
307
DEBUG_LOG(("nsEnigMimeListener::GetContentCharset: %s\n", mContentCharset.get()));
312
nsEnigMimeListener::GetContentBoundary(nsACString &aContentBoundary)
314
aContentBoundary = mContentBoundary;
315
DEBUG_LOG(("nsEnigMimeListener::GetContentBoundary: %s\n", mContentBoundary.get()));
320
nsEnigMimeListener::GetContentProtocol(nsACString &aContentProtocol)
322
aContentProtocol = mContentProtocol;
323
DEBUG_LOG(("nsEnigMimeListener::GetContentProtocol: %s\n", mContentProtocol.get()));
328
nsEnigMimeListener::GetContentMicalg(nsACString &aContentMicalg)
330
aContentMicalg = mContentMicalg;
331
DEBUG_LOG(("nsEnigMimeListener::GetContentMicalg: %s\n", mContentMicalg.get()));
336
nsEnigMimeListener::GetContentEncoding(nsACString &aContentEncoding)
338
aContentEncoding = mContentEncoding;
339
DEBUG_LOG(("nsEnigMimeListener::GetContentEncoding: %s\n", mContentEncoding.get()));
344
nsEnigMimeListener::GetContentDisposition(nsACString &aContentDisposition)
346
aContentDisposition = mContentDisposition;
347
DEBUG_LOG(("nsEnigMimeListener::GetContentDisposition: %s\n", mContentDisposition.get()));
352
nsEnigMimeListener::GetContentLength(PRInt32 *aContentLength)
354
DEBUG_LOG(("nsEnigMimeListener::GetContentLength: \n"));
355
*aContentLength = mContentLength;
359
///////////////////////////////////////////////////////////////////////////////
360
// nsIRequestObserver methods
361
///////////////////////////////////////////////////////////////////////////////
364
nsEnigMimeListener::OnStartRequest(nsIRequest *aRequest,
365
nsISupports *aContext)
367
DEBUG_LOG(("nsEnigMimeListener::OnStartRequest: (%p)\n", this));
370
return NS_ERROR_NOT_INITIALIZED;
376
nsEnigMimeListener::OnStopRequest(nsIRequest* aRequest,
377
nsISupports* aContext,
382
DEBUG_LOG(("nsEnigMimeListener::OnStopRequest: (%p)\n", this));
384
// Ensure that OnStopRequest call chain does not break by failing softly
386
if (!mRequestStarted) {
388
if (mHeadersFinalCR) {
389
// Handle special case of terminating CR with no content
390
mHeadersFinalCR = PR_FALSE;
401
rv = StartRequest(aRequest, aContext);
403
aStatus = NS_BINDING_ABORTED;
407
// Clear decoder buffer
408
MimeDecoderDestroy(mDecoderData, PR_FALSE);
409
mDecoderData = nsnull;
413
rv = mListener->OnStopRequest(aRequest,
414
mContext ? mContext.get() : aContext,
417
aStatus = NS_BINDING_ABORTED;
420
// Release owning refs
424
return (aStatus == NS_BINDING_ABORTED) ? NS_ERROR_FAILURE : NS_OK;
427
///////////////////////////////////////////////////////////////////////////////
428
// nsIStreamListener method
429
///////////////////////////////////////////////////////////////////////////////
432
nsEnigMimeListener::OnDataAvailable(nsIRequest* aRequest,
433
nsISupports* aContext,
434
nsIInputStream *aInputStream,
435
PRUint32 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
PRBool lastSegment = (bytesAvailable <= count);
547
PRUint32 scanLen = lastSegment ? bytesAvailable : count;
549
PRBool 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;
680
nsEnigMimeListener::ParseMimeHeaders(const char* mimeHeaders, PRUint32 count)
682
DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders, count=%d\n", count));
684
// Copy headers string
685
nsCAutoString headers(mimeHeaders, count);
687
// Replace CRLF with just LF
688
headers.ReplaceSubstring("\r\n", "\n");
690
// Replace CR with LF (for MAC-style line endings)
691
headers.ReplaceChar('\r', '\n');
693
// Eliminate all leading whitespace (including linefeeds)
694
headers.Trim(" \t\n", PR_TRUE, PR_FALSE);
696
if (headers.Length() <= 3) {
697
// No headers to parse
701
// Handle continuation of MIME headers, i.e., newline followed by whitespace
702
headers.ReplaceSubstring( "\n ", " ");
703
headers.ReplaceSubstring( "\n\t", "\t");
705
//DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders: headers='%s'\n", headers.get()));
708
while (offset < headers.Length()) {
709
PRInt32 lineEnd = headers.FindChar('\n', offset);
711
if (lineEnd == kNotFound) {
712
// Header line terminator not found
713
NS_NOTREACHED("lineEnd == kNotFound");
717
// Normal exit if empty header line
718
if (lineEnd == (int)offset)
722
ParseHeader((headers.get())+offset, lineEnd - offset);
727
if (mDecodeContent) {
729
if (mContentEncoding.EqualsIgnoreCase("base64")) {
731
mDecoderData = MimeB64DecoderInit(EnigMimeListener_write, (void*) this);
733
} else if (mContentEncoding.EqualsIgnoreCase("quoted-printable")) {
735
mDecoderData = MimeQPDecoderInit(EnigMimeListener_write, (void*) this);
742
nsEnigMimeListener::ParseHeader(const char* header, PRUint32 count)
745
//DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", header));
747
if (!header || (count <= 0) )
750
// Create header string
751
nsCAutoString headerStr(header, count);
754
colonOffset = headerStr.FindChar(':');
755
if (colonOffset == kNotFound)
758
// Null header key not allowed
759
if (colonOffset == 0)
762
// Extract header key (not case-sensitive)
763
nsCAutoString headerKey;
764
headerStr.Left(headerKey, colonOffset);
765
ToLowerCase(headerKey);
767
// Extract header value, trimming leading/trailing whitespace
769
headerStr.Right(buf, headerStr.Length() - colonOffset - 1);
772
//DEBUG_LOG(("nsEnigMimeListener::ParseHeader: %s: %s\n", headerKey.get(), buf.get()));
774
PRInt32 semicolonOffset = buf.FindChar(';');
776
nsCAutoString headerValue;
777
if (semicolonOffset == kNotFound) {
779
headerValue = buf.get();
782
// Extract value to left of parameters
783
buf.Left(headerValue, semicolonOffset);
786
// Trim leading and trailing spaces in header value
787
headerValue.Trim(" ");
789
if (headerKey.Equals("content-type")) {
790
mContentType = headerValue;
792
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentType=%s\n",
793
mContentType.get()));
795
if (!buf.IsEmpty()) {
796
char *charset = MimeHeaders_get_parameter(buf.get(),
797
HEADER_PARM_CHARSET, NULL, NULL);
798
char *boundary = MimeHeaders_get_parameter(buf.get(),
799
HEADER_PARM_BOUNDARY, NULL, NULL);
800
char *protocol = MimeHeaders_get_parameter(buf.get(),
801
PARAM_PROTOCOL, NULL, NULL);
802
char *micalg = MimeHeaders_get_parameter(buf.get(),
803
PARAM_MICALG, NULL, NULL);
806
mContentCharset = charset;
809
mContentBoundary = boundary;
812
mContentProtocol = protocol;
815
mContentMicalg = micalg;
822
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentCharset=%s\n",
823
mContentCharset.get()));
825
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentBoundary=%s\n",
826
mContentBoundary.get()));
828
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentProtocol=%s\n",
829
mContentProtocol.get()));
831
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentMicalg=%s\n",
832
mContentMicalg.get()));
835
} else if (headerKey.Equals("content-transfer-encoding")) {
836
mContentEncoding = buf;
837
ToLowerCase(mContentEncoding);
839
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentEncoding=%s\n",
840
mContentEncoding.get()));
842
} else if (headerKey.Equals("content-disposition")) {
843
mContentDisposition = buf;
845
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentDisposition=%s\n",
846
mContentDisposition.get()));
848
} else if (headerKey.Equals("content-length")) {
850
PRInt32 value = headerValue.ToInteger(&status);
852
if (NS_SUCCEEDED((nsresult) status))
853
mContentLength = value;
855
DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContenLengtht=%d\n",
863
///////////////////////////////////////////////////////////////////////////////
864
// nsIInputStream methods
865
///////////////////////////////////////////////////////////////////////////////
868
nsEnigMimeListener::Available(PRUint32* _retval)
871
return NS_ERROR_NULL_POINTER;
873
*_retval = (mStreamLength > mStreamOffset) ?
874
mStreamLength - mStreamOffset : 0;
876
DEBUG_LOG(("nsEnigMimeListener::Available: (%p) %d\n", this, *_retval));
882
nsEnigMimeListener::Read(char* buf, PRUint32 count,
885
DEBUG_LOG(("nsEnigMimeListener::Read: (%p) %d\n", this, count));
887
if (!buf || !readCount)
888
return NS_ERROR_NULL_POINTER;
890
PRInt32 avail = (mStreamLength > mStreamOffset) ?
891
mStreamLength - mStreamOffset : 0;
893
*readCount = ((PRUint32) avail > count) ? count : avail;
896
memcpy(buf, mStreamBuf+mStreamOffset, *readCount);
897
mStreamOffset += *readCount;
900
if (mStreamOffset >= mStreamLength) {
908
nsEnigMimeListener::ReadSegments(nsWriteSegmentFun writer,
909
void * aClosure, PRUint32 count,
912
DEBUG_LOG(("nsEnigMimeListener::ReadSegments: %d\n", count));
915
return NS_ERROR_NULL_POINTER;
917
PRInt32 avail = (mStreamLength > mStreamOffset) ?
918
mStreamLength - mStreamOffset : 0;
920
PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
926
nsresult rv = writer((nsIInputStream*)(this),
927
aClosure, mStreamBuf+mStreamOffset,
928
mStreamOffset, readyCount, readCount);
932
mStreamOffset += *readCount;
935
if (mStreamOffset >= mStreamLength) {
943
nsEnigMimeListener::IsNonBlocking(PRBool *aNonBlocking)
945
DEBUG_LOG(("nsEnigMimeListener::IsNonBlocking: \n"));
947
*aNonBlocking = PR_TRUE;
952
nsEnigMimeListener::GetSubPartTreatment(PRBool* aSubPartTreatment)
954
*aSubPartTreatment = mSubPartTreatment;
959
nsEnigMimeListener::SetSubPartTreatment(PRBool aSubPartTreatment)
961
DEBUG_LOG(("nsEnigMimeListener::SetSubPartTreatment: %d\n", aSubPartTreatment));
963
mSubPartTreatment = aSubPartTreatment;
968
nsEnigMimeListener::Close()
970
DEBUG_LOG(("nsEnigMimeListener::Close: (%p)\n", this));