~ubuntu-branches/ubuntu/oneiric/enigmail/oneiric-updates

« back to all changes in this revision

Viewing changes to extensions/enigmail/src/nsEnigMimeListener.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Sack
  • Date: 2010-04-10 01:42:24 UTC
  • Revision ID: james.westby@ubuntu.com-20100410014224-fbq9ui5x3b0h2t36
Tags: 2:1.0.1-0ubuntu1
* First releaase of enigmail 1.0.1 for tbird/icedove 3
  (LP: #527138)
* redo packaging from scratch 
  + add debian/make-orig target that uses xulrunner provided
    buildsystem + enigmail tarball to produce a proper orig.tar.gz
  + use debhelper 7 with mozilla-devscripts
  + use debian source format 3.0 (quilt)
  + patch enigmail to use frozen API only
    - add debian/patches/frozen_api.diff
  + patch build system to not link against -lxul - which isnt
    available for sdks produced by all-static apps like tbird
    - add debian/patches/build_system_dont_link_libxul.diff
  + add minimal build-depends to control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ***** BEGIN LICENSE BLOCK *****
 
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
3
 *
 
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/
 
8
 *
 
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
 
12
 * MPL.
 
13
 *
 
14
 * The Original Code is Enigmail.
 
15
 *
 
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.
 
20
 *
 
21
 * Contributor(s):
 
22
 * Patrick Brunschwig <patrick.brunschwig@gmx.net>
 
23
 *
 
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.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
 
 
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 */
 
41
 
 
42
#include "enigmail.h"
 
43
#include "prlog.h"
 
44
#include "nsCOMPtr.h"
 
45
#include "nsAutoLock.h"
 
46
#include "nsIInputStream.h"
 
47
#include "nsIThread.h"
 
48
#include "nsString.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"
 
55
#endif
 
56
#undef MOZILLA_INTERNAL_API
 
57
 
 
58
#include "nsEnigMimeListener.h"
 
59
 
 
60
#ifdef PR_LOGGING
 
61
PRLogModuleInfo* gEnigMimeListenerLog = NULL;
 
62
#endif
 
63
 
 
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)
 
67
 
 
68
#define NS_PIPE_CONSOLE_BUFFER_SIZE   (1024)
 
69
 
 
70
static const PRUint32 kCharMax = 1024;
 
71
 
 
72
#define MK_MIME_ERROR_WRITING_FILE -1
 
73
 
 
74
///////////////////////////////////////////////////////////////////////////////
 
75
 
 
76
// nsEnigMimeListener implementation
 
77
 
 
78
// nsISupports implementation
 
79
NS_IMPL_THREADSAFE_ISUPPORTS4(nsEnigMimeListener,
 
80
                              nsIEnigMimeListener,
 
81
                              nsIRequestObserver,
 
82
                              nsIStreamListener,
 
83
                              nsIInputStream)
 
84
 
 
85
 
 
86
// nsEnigMimeListener implementation
 
87
nsEnigMimeListener::nsEnigMimeListener()
 
88
  : mInitialized(PR_FALSE),
 
89
    mRequestStarted(PR_FALSE),
 
90
    mSkipHeaders(PR_FALSE),
 
91
    mSkipBody(PR_FALSE),
 
92
 
 
93
    mContentType(""),
 
94
    mContentCharset(""),
 
95
    mContentBoundary(""),
 
96
    mContentProtocol(""),
 
97
    mContentMicalg(""),
 
98
 
 
99
    mContentEncoding(""),
 
100
    mContentDisposition(""),
 
101
    mContentLength(-1),
 
102
 
 
103
    mDecodeContent(PR_FALSE),
 
104
    mDecoderData(nsnull),
 
105
 
 
106
    mLinebreak(""),
 
107
    mHeaders(""),
 
108
    mDataStr(""),
 
109
    mHeaderSearchCounter(0),
 
110
 
 
111
    mHeadersFinalCR(PR_FALSE),
 
112
    mHeadersLinebreak(2),
 
113
 
 
114
    mMaxHeaderBytes(0),
 
115
    mDataOffset(0),
 
116
 
 
117
    mStreamBuf(nsnull),
 
118
    mStreamOffset(0),
 
119
    mStreamLength(0),
 
120
    mSubPartTreatment(PR_FALSE),
 
121
 
 
122
    mListener(nsnull),
 
123
    mContext(nsnull)
 
124
{
 
125
    NS_INIT_ISUPPORTS();
 
126
 
 
127
#ifdef PR_LOGGING
 
128
  if (gEnigMimeListenerLog == nsnull) {
 
129
    gEnigMimeListenerLog = PR_NewLogModule("nsEnigMimeListener");
 
130
  }
 
131
#endif
 
132
 
 
133
#ifdef FORCE_PR_LOG
 
134
  nsresult rv;
 
135
  nsCOMPtr<nsIThread> myThread;
 
136
  rv = ENIG_GET_THREAD(myThread);
 
137
  DEBUG_LOG(("nsEnigMimeListener:: <<<<<<<<< CTOR(%p): myThread=%p\n",
 
138
         this, myThread.get()));
 
139
#endif
 
140
}
 
141
 
 
142
 
 
143
nsEnigMimeListener::~nsEnigMimeListener()
 
144
{
 
145
  nsresult rv;
 
146
#ifdef FORCE_PR_LOG
 
147
  nsCOMPtr<nsIThread> myThread;
 
148
  rv = ENIG_GET_THREAD(myThread);
 
149
  DEBUG_LOG(("nsEnigMimeListener:: >>>>>>>>> DTOR(%p): myThread=%p\n",
 
150
         this, myThread.get()));
 
151
#endif
 
152
 
 
153
  if (mDecoderData) {
 
154
    // Clear decoder buffer
 
155
    MimeDecoderDestroy(mDecoderData, PR_FALSE);
 
156
    mDecoderData = nsnull;
 
157
  }
 
158
 
 
159
  // Release owning refs
 
160
  mListener = nsnull;
 
161
  mContext = nsnull;
 
162
}
 
163
 
 
164
 
 
165
///////////////////////////////////////////////////////////////////////////////
 
166
// nsIEnigMimeListener methods
 
167
///////////////////////////////////////////////////////////////////////////////
 
168
 
 
169
NS_IMETHODIMP
 
170
nsEnigMimeListener::Init(nsIStreamListener* listener, nsISupports* ctxt,
 
171
                         PRUint32 maxHeaderBytes, PRBool skipHeaders,
 
172
                         PRBool skipBody, PRBool decodeContent)
 
173
{
 
174
  DEBUG_LOG(("nsEnigMimeListener::Init: (%p) %d, %d, %d, %d\n", this,
 
175
             maxHeaderBytes, skipHeaders, skipBody, decodeContent));
 
176
 
 
177
  if (!listener)
 
178
    return NS_ERROR_NULL_POINTER;
 
179
 
 
180
  mListener = listener;
 
181
  mContext = ctxt;
 
182
 
 
183
  mMaxHeaderBytes = maxHeaderBytes;
 
184
 
 
185
  mSkipHeaders = skipHeaders;
 
186
  mSkipBody = skipBody;
 
187
  mDecodeContent = decodeContent;
 
188
 
 
189
  // There is implicitly a newline preceding the first character
 
190
  mHeadersLinebreak = 2;
 
191
  mHeadersFinalCR = PR_FALSE;
 
192
 
 
193
  mInitialized = PR_TRUE;
 
194
 
 
195
  return NS_OK;
 
196
}
 
197
 
 
198
 
 
199
NS_IMETHODIMP
 
200
nsEnigMimeListener::Write(const char* buf, PRUint32 count,
 
201
                          nsIRequest* aRequest, nsISupports* aContext)
 
202
{
 
203
  nsresult rv;
 
204
 
 
205
  DEBUG_LOG(("nsEnigMimeListener::Write: (%p) %d\n", this, count));
 
206
 
 
207
  if (mRequestStarted)
 
208
    return Transmit(buf, count, aRequest, aContext);
 
209
 
 
210
  // Search for headers
 
211
  PRBool startingRequest = HeaderSearch(buf, count);
 
212
  if (!startingRequest)
 
213
    return NS_OK;
 
214
 
 
215
  rv = StartRequest(aRequest, aContext);
 
216
  if (NS_FAILED(rv))
 
217
    return rv;
 
218
 
 
219
  return NS_OK;
 
220
}
 
221
 
 
222
static nsresult
 
223
EnigMimeListener_write(const char *buf, PRInt32 size, void *closure)
 
224
{
 
225
  DEBUG_LOG(("nsEnigMimeListener::EnigMimeListener_write: (%p) %d\n", closure, size));
 
226
 
 
227
  if (!closure)
 
228
    return NS_ERROR_FAILURE;
 
229
 
 
230
  nsEnigMimeListener* enigMimeListener = (nsEnigMimeListener *) closure;
 
231
 
 
232
  return enigMimeListener->SendStream(buf, size, nsnull, nsnull);
 
233
}
 
234
 
 
235
 
 
236
NS_METHOD
 
237
nsEnigMimeListener::Transmit(const char* buf, PRUint32 count,
 
238
                             nsIRequest* aRequest, nsISupports* aContext)
 
239
{
 
240
  DEBUG_LOG(("nsEnigMimeListener::Transmit: (%p) %d\n", this, count));
 
241
 
 
242
  if (!mDecoderData) {
 
243
    return SendStream(buf, count, aRequest, aContext);
 
244
  }
 
245
 
 
246
  // Decode data before transmitting to listener
 
247
  int status = MimeDecoderWrite(mDecoderData, buf, count);
 
248
 
 
249
  return (status == 0) ? NS_OK : NS_ERROR_FAILURE;
 
250
}
 
251
 
 
252
 
 
253
NS_METHOD
 
254
nsEnigMimeListener::SendStream(const char* buf, PRUint32 count,
 
255
                               nsIRequest* aRequest, nsISupports* aContext)
 
256
{
 
257
  nsresult rv;
 
258
 
 
259
  DEBUG_LOG(("nsEnigMimeListener::SendStream: (%p) %d\n", this, count));
 
260
 
 
261
  if (!mListener)
 
262
    return NS_OK;
 
263
 
 
264
  // Transmit data to listener
 
265
  mStreamBuf = buf;
 
266
  mStreamOffset = 0;
 
267
  mStreamLength = count;
 
268
 
 
269
  rv = mListener->OnDataAvailable(aRequest,
 
270
                                  mContext ? mContext.get() : aContext,
 
271
                                  (nsIInputStream*)(this),
 
272
                                  0, count);
 
273
  Close();
 
274
 
 
275
  return rv;
 
276
}
 
277
 
 
278
 
 
279
NS_IMETHODIMP
 
280
nsEnigMimeListener::GetHeaders(nsACString &aHeaders)
 
281
{
 
282
  aHeaders = mHeaders;
 
283
  DEBUG_LOG(("nsEnigMimeListener::GetHeaders: %d\n", mHeaders.Length()));
 
284
  return NS_OK;
 
285
}
 
286
 
 
287
NS_IMETHODIMP
 
288
nsEnigMimeListener::GetLinebreak(nsACString &aLinebreak)
 
289
{
 
290
  aLinebreak = mLinebreak;
 
291
  DEBUG_LOG(("nsEnigMimeListener::GetLinebreak: %d\n", mLinebreak.Length()));
 
292
  return NS_OK;
 
293
}
 
294
 
 
295
NS_IMETHODIMP
 
296
nsEnigMimeListener::GetContentType(nsACString &aContentType)
 
297
{
 
298
  aContentType = mContentType;
 
299
  DEBUG_LOG(("nsEnigMimeListener::GetContentType: %s\n", mContentType.get()));
 
300
  return NS_OK;
 
301
}
 
302
 
 
303
NS_IMETHODIMP
 
304
nsEnigMimeListener::GetContentCharset(nsACString &aContentCharset)
 
305
{
 
306
  aContentCharset = mContentCharset;
 
307
  DEBUG_LOG(("nsEnigMimeListener::GetContentCharset: %s\n", mContentCharset.get()));
 
308
  return NS_OK;
 
309
}
 
310
 
 
311
NS_IMETHODIMP
 
312
nsEnigMimeListener::GetContentBoundary(nsACString &aContentBoundary)
 
313
{
 
314
  aContentBoundary = mContentBoundary;
 
315
  DEBUG_LOG(("nsEnigMimeListener::GetContentBoundary: %s\n", mContentBoundary.get()));
 
316
  return NS_OK;
 
317
}
 
318
 
 
319
NS_IMETHODIMP
 
320
nsEnigMimeListener::GetContentProtocol(nsACString &aContentProtocol)
 
321
{
 
322
  aContentProtocol = mContentProtocol;
 
323
  DEBUG_LOG(("nsEnigMimeListener::GetContentProtocol: %s\n", mContentProtocol.get()));
 
324
  return NS_OK;
 
325
}
 
326
 
 
327
NS_IMETHODIMP
 
328
nsEnigMimeListener::GetContentMicalg(nsACString &aContentMicalg)
 
329
{
 
330
  aContentMicalg = mContentMicalg;
 
331
  DEBUG_LOG(("nsEnigMimeListener::GetContentMicalg: %s\n", mContentMicalg.get()));
 
332
  return NS_OK;
 
333
}
 
334
 
 
335
NS_IMETHODIMP
 
336
nsEnigMimeListener::GetContentEncoding(nsACString &aContentEncoding)
 
337
{
 
338
  aContentEncoding = mContentEncoding;
 
339
  DEBUG_LOG(("nsEnigMimeListener::GetContentEncoding: %s\n", mContentEncoding.get()));
 
340
  return NS_OK;
 
341
}
 
342
 
 
343
NS_IMETHODIMP
 
344
nsEnigMimeListener::GetContentDisposition(nsACString &aContentDisposition)
 
345
{
 
346
  aContentDisposition = mContentDisposition;
 
347
  DEBUG_LOG(("nsEnigMimeListener::GetContentDisposition: %s\n", mContentDisposition.get()));
 
348
  return NS_OK;
 
349
}
 
350
 
 
351
NS_IMETHODIMP
 
352
nsEnigMimeListener::GetContentLength(PRInt32 *aContentLength)
 
353
{
 
354
  DEBUG_LOG(("nsEnigMimeListener::GetContentLength: \n"));
 
355
  *aContentLength = mContentLength;
 
356
  return NS_OK;
 
357
}
 
358
 
 
359
///////////////////////////////////////////////////////////////////////////////
 
360
// nsIRequestObserver methods
 
361
///////////////////////////////////////////////////////////////////////////////
 
362
 
 
363
NS_IMETHODIMP
 
364
nsEnigMimeListener::OnStartRequest(nsIRequest *aRequest,
 
365
                                   nsISupports *aContext)
 
366
{
 
367
  DEBUG_LOG(("nsEnigMimeListener::OnStartRequest: (%p)\n", this));
 
368
 
 
369
  if (!mInitialized)
 
370
    return NS_ERROR_NOT_INITIALIZED;
 
371
 
 
372
  return NS_OK;
 
373
}
 
374
 
 
375
NS_IMETHODIMP
 
376
nsEnigMimeListener::OnStopRequest(nsIRequest* aRequest,
 
377
                                  nsISupports* aContext,
 
378
                                  nsresult aStatus)
 
379
{
 
380
  nsresult rv = NS_OK;
 
381
 
 
382
  DEBUG_LOG(("nsEnigMimeListener::OnStopRequest: (%p)\n", this));
 
383
 
 
384
  // Ensure that OnStopRequest call chain does not break by failing softly
 
385
 
 
386
  if (!mRequestStarted) {
 
387
 
 
388
    if (mHeadersFinalCR) {
 
389
      // Handle special case of terminating CR with no content
 
390
      mHeadersFinalCR = PR_FALSE;
 
391
 
 
392
      mLinebreak = "\r";
 
393
      mHeaders = mDataStr;
 
394
 
 
395
      if (mSkipHeaders) {
 
396
        // Skip headers
 
397
        mDataStr = "";
 
398
      }
 
399
    }
 
400
 
 
401
    rv = StartRequest(aRequest, aContext);
 
402
    if (NS_FAILED(rv))
 
403
      aStatus = NS_BINDING_ABORTED;
 
404
  }
 
405
 
 
406
  if (mDecoderData) {
 
407
    // Clear decoder buffer
 
408
    MimeDecoderDestroy(mDecoderData, PR_FALSE);
 
409
    mDecoderData = nsnull;
 
410
  }
 
411
 
 
412
  if (mListener) {
 
413
    rv = mListener->OnStopRequest(aRequest,
 
414
                                  mContext ? mContext.get() : aContext,
 
415
                                  aStatus);
 
416
    if (NS_FAILED(rv))
 
417
      aStatus = NS_BINDING_ABORTED;
 
418
  }
 
419
 
 
420
  // Release owning refs
 
421
  mListener = nsnull;
 
422
  mContext = nsnull;
 
423
 
 
424
  return (aStatus == NS_BINDING_ABORTED) ? NS_ERROR_FAILURE : NS_OK;
 
425
}
 
426
 
 
427
///////////////////////////////////////////////////////////////////////////////
 
428
// nsIStreamListener method
 
429
///////////////////////////////////////////////////////////////////////////////
 
430
 
 
431
NS_IMETHODIMP
 
432
nsEnigMimeListener::OnDataAvailable(nsIRequest* aRequest,
 
433
                                    nsISupports* aContext,
 
434
                                    nsIInputStream *aInputStream,
 
435
                                    PRUint32 aSourceOffset,
 
436
                                    PRUint32 aLength)
 
437
{
 
438
  nsresult rv = NS_OK;
 
439
 
 
440
  DEBUG_LOG(("nsEnigMimeListener::OnDataAvailable: (%p) %d\n", this, aLength));
 
441
 
 
442
  if (!mInitialized)
 
443
    return NS_ERROR_NOT_INITIALIZED;
 
444
 
 
445
  char buf[kCharMax];
 
446
  PRUint32 readCount, readMax;
 
447
 
 
448
  while ((aLength > 0) && (!mRequestStarted || mDecoderData) ) {
 
449
    // Searching for headers or decoding content
 
450
 
 
451
    readMax = (aLength < kCharMax) ? aLength : kCharMax;
 
452
    rv = aInputStream->Read((char *) buf, readMax, &readCount);
 
453
    if (NS_FAILED(rv)){
 
454
      ERROR_LOG(("nsEnigMimeListener::OnDataAvailable: Error in reading from input stream, %x\n", rv));
 
455
      return rv;
 
456
    }
 
457
 
 
458
    if (readCount <= 0)
 
459
      break;
 
460
 
 
461
    aLength -= readCount;
 
462
    aSourceOffset += readCount;
 
463
 
 
464
    rv = Write(buf, readCount, aRequest, aContext);
 
465
    if (NS_FAILED(rv))
 
466
      return rv;
 
467
  }
 
468
 
 
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;
 
476
 
 
477
    if (NS_FAILED(rv))
 
478
      return rv;
 
479
  }
 
480
 
 
481
  return NS_OK;
 
482
}
 
483
 
 
484
 
 
485
NS_IMETHODIMP
 
486
nsEnigMimeListener::StartRequest(nsIRequest* aRequest, nsISupports* aContext)
 
487
{
 
488
  nsresult rv;
 
489
 
 
490
  DEBUG_LOG(("nsEnigMimeListener::StartRequest: (%p)\n", this));
 
491
 
 
492
  if (!mHeaders.IsEmpty()) {
 
493
    // Try to parse headers
 
494
    ParseMimeHeaders(mHeaders.get(), mHeaders.Length());
 
495
  }
 
496
 
 
497
  if (mListener) {
 
498
    rv = mListener->OnStartRequest(aRequest,
 
499
                                   mContext ? mContext.get() : aContext);
 
500
    if (NS_FAILED(rv))
 
501
      return rv;
 
502
  }
 
503
 
 
504
  mRequestStarted = PR_TRUE;
 
505
 
 
506
  if (mHeaders.IsEmpty() && mSkipBody) {
 
507
    // No headers terminated and skipping body; so discard whatever we have
 
508
    mDataStr = "";
 
509
  }
 
510
 
 
511
  if (!mDataStr.IsEmpty()) {
 
512
    // Transmit header/body data already in buffer
 
513
    nsCAutoString temStr( mDataStr );
 
514
 
 
515
    mDataOffset += mDataStr.Length();
 
516
    mDataStr = "";
 
517
 
 
518
    rv = Transmit(temStr.get(), temStr.Length(), aRequest, aContext);
 
519
    if (NS_FAILED(rv))
 
520
      return rv;
 
521
  }
 
522
 
 
523
  return NS_OK;
 
524
}
 
525
 
 
526
 
 
527
PRBool
 
528
nsEnigMimeListener::HeaderSearch(const char* buf, PRUint32 count)
 
529
{
 
530
  DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: (%p) count=%d\n", this, count));
 
531
 
 
532
  mHeaderSearchCounter++;
 
533
 
 
534
  if (mMaxHeaderBytes <= 0) {
 
535
    // Not looking for MIME headers; start request immediately
 
536
    return PR_TRUE;
 
537
  }
 
538
 
 
539
  if (!count)
 
540
    return PR_FALSE;
 
541
 
 
542
  PRUint32 bytesAvailable = mMaxHeaderBytes - mDataStr.Length();
 
543
  NS_ASSERTION(bytesAvailable > 0, "bytesAvailable <= 0");
 
544
 
 
545
  PRBool lastSegment = (bytesAvailable <= count);
 
546
 
 
547
  PRUint32 scanLen = lastSegment ? bytesAvailable : count;
 
548
 
 
549
  PRBool headersFound = PR_FALSE;
 
550
  PRUint32 offset = 0;
 
551
  PRUint32 startOffset = 0;
 
552
  PRUint32 j = 0;
 
553
  char ch;
 
554
  if (mSubPartTreatment) {
 
555
    // FIXME:
 
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"));
 
562
    ch='\n';
 
563
    while(j<scanLen-3) {
 
564
      if (((ch=='\n') || (ch=='\r')) &&
 
565
          (buf[j]=='-') &&
 
566
          (buf[j+1]=='-') &&
 
567
          (buf[j+2]!='\n') &&
 
568
          (buf[j+2]!='\r'))
 
569
      {
 
570
          startOffset = j;
 
571
          DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: startOffset=%d\n",startOffset));
 
572
          break;
 
573
      }
 
574
      ch=buf[j];
 
575
      j++;
 
576
    }
 
577
 
 
578
    // set j=startOffset needed if startOffset == 0!
 
579
    j=startOffset;
 
580
/*
 
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());
 
585
    mHeaders = cType;
 
586
    if (mSkipHeaders)
 
587
      mDataStr = "";
 
588
    if (!mSkipBody)
 
589
      mDataStr.Append(buf, count);
 
590
 
 
591
    mHeadersLinebreak = 0;
 
592
    mLinebreak = "\n";
 
593
*/
 
594
    mSubPartTreatment = PR_FALSE;
 
595
    // return PR_TRUE;
 
596
  }
 
597
 
 
598
  while (j<scanLen) {
 
599
    ch = buf[j];
 
600
 
 
601
    if (mHeadersFinalCR) {
 
602
      // End-of-headers found
 
603
      mHeadersFinalCR = PR_FALSE;
 
604
 
 
605
      if (ch == '\n') {
 
606
        offset = j+1;
 
607
        mLinebreak = "\r\n";
 
608
        DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CRLF"));
 
609
 
 
610
      } else {
 
611
        offset = j;
 
612
        mLinebreak = "\r";
 
613
        DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CR"));
 
614
      }
 
615
 
 
616
      headersFound = PR_TRUE;
 
617
      break;
 
618
 
 
619
    }
 
620
 
 
621
    if (ch == '\n') {
 
622
 
 
623
      if (mHeadersLinebreak == 2) {
 
624
        // End-of-headers found
 
625
        headersFound = PR_TRUE;
 
626
 
 
627
        offset = j+1;
 
628
        mLinebreak = "\n";
 
629
        DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final LF"));
 
630
        break;
 
631
      }
 
632
 
 
633
      mHeadersLinebreak = 2;
 
634
 
 
635
    } else if (ch == '\r') {
 
636
 
 
637
      if (mHeadersLinebreak > 0) {
 
638
        // Final CR
 
639
        mHeadersFinalCR = PR_TRUE;
 
640
      } else {
 
641
        mHeadersLinebreak = 1;
 
642
      }
 
643
 
 
644
    } else {
 
645
      mHeadersLinebreak = 0;
 
646
    }
 
647
 
 
648
    j++;
 
649
  }
 
650
 
 
651
  DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: offset=%d\n", offset));
 
652
 
 
653
  if (headersFound) {
 
654
    // Copy headers out of stream buffer
 
655
    if (offset > 0)
 
656
      mDataStr.Append(buf+startOffset, offset-startOffset);
 
657
 
 
658
    mHeaders = mDataStr;
 
659
 
 
660
    if (mSkipHeaders) {
 
661
      // Skip headers
 
662
      mDataStr = "";
 
663
    }
 
664
 
 
665
    if (!mSkipBody && (offset < count)) {
 
666
      // Copy remaining data into stream buffer
 
667
     mDataStr.Append(buf+offset, count-offset);
 
668
    }
 
669
 
 
670
  } else if (!lastSegment) {
 
671
    // Save headers data
 
672
    mDataStr.Append(buf, count);
 
673
  }
 
674
 
 
675
  return headersFound || lastSegment;
 
676
}
 
677
 
 
678
 
 
679
void
 
680
nsEnigMimeListener::ParseMimeHeaders(const char* mimeHeaders, PRUint32 count)
 
681
{
 
682
  DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders, count=%d\n", count));
 
683
 
 
684
  // Copy headers string
 
685
  nsCAutoString headers(mimeHeaders, count);
 
686
 
 
687
  // Replace CRLF with just LF
 
688
  headers.ReplaceSubstring("\r\n", "\n");
 
689
 
 
690
  // Replace CR with LF (for MAC-style line endings)
 
691
  headers.ReplaceChar('\r', '\n');
 
692
 
 
693
  // Eliminate all leading whitespace (including linefeeds)
 
694
  headers.Trim(" \t\n", PR_TRUE, PR_FALSE);
 
695
 
 
696
  if (headers.Length() <= 3) {
 
697
    // No headers to parse
 
698
    return;
 
699
  }
 
700
 
 
701
  // Handle continuation of MIME headers, i.e., newline followed by whitespace
 
702
  headers.ReplaceSubstring( "\n ",  " ");
 
703
  headers.ReplaceSubstring( "\n\t", "\t");
 
704
 
 
705
  //DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders: headers='%s'\n", headers.get()));
 
706
 
 
707
  PRUint32 offset = 0;
 
708
  while (offset < headers.Length()) {
 
709
    PRInt32 lineEnd = headers.FindChar('\n', offset);
 
710
 
 
711
    if (lineEnd == kNotFound) {
 
712
      // Header line terminator not found
 
713
      NS_NOTREACHED("lineEnd == kNotFound");
 
714
      return;
 
715
    }
 
716
 
 
717
    // Normal exit if empty header line
 
718
    if (lineEnd == (int)offset)
 
719
      break;
 
720
 
 
721
    // Parse header line
 
722
    ParseHeader((headers.get())+offset, lineEnd - offset);
 
723
 
 
724
    offset = lineEnd+1;
 
725
  }
 
726
 
 
727
  if (mDecodeContent) {
 
728
    // Decode data
 
729
    if (mContentEncoding.EqualsIgnoreCase("base64")) {
 
730
 
 
731
      mDecoderData = MimeB64DecoderInit(EnigMimeListener_write, (void*) this);
 
732
 
 
733
    } else if (mContentEncoding.EqualsIgnoreCase("quoted-printable")) {
 
734
 
 
735
      mDecoderData = MimeQPDecoderInit(EnigMimeListener_write, (void*) this);
 
736
    }
 
737
  }
 
738
  return;
 
739
}
 
740
 
 
741
void
 
742
nsEnigMimeListener::ParseHeader(const char* header, PRUint32 count)
 
743
{
 
744
 
 
745
  //DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", header));
 
746
 
 
747
  if (!header || (count <= 0) )
 
748
    return;
 
749
 
 
750
  // Create header string
 
751
  nsCAutoString headerStr(header, count);
 
752
 
 
753
  PRInt32 colonOffset;
 
754
  colonOffset = headerStr.FindChar(':');
 
755
  if (colonOffset == kNotFound)
 
756
    return;
 
757
 
 
758
  // Null header key not allowed
 
759
  if (colonOffset == 0)
 
760
    return;
 
761
 
 
762
  // Extract header key (not case-sensitive)
 
763
  nsCAutoString headerKey;
 
764
  headerStr.Left(headerKey, colonOffset);
 
765
  ToLowerCase(headerKey);
 
766
 
 
767
  // Extract header value, trimming leading/trailing whitespace
 
768
  nsCAutoString buf;
 
769
  headerStr.Right(buf, headerStr.Length() - colonOffset - 1);
 
770
  buf.Trim(" ");
 
771
 
 
772
  //DEBUG_LOG(("nsEnigMimeListener::ParseHeader: %s: %s\n", headerKey.get(), buf.get()));
 
773
 
 
774
  PRInt32 semicolonOffset = buf.FindChar(';');
 
775
 
 
776
  nsCAutoString headerValue;
 
777
  if (semicolonOffset == kNotFound) {
 
778
    // No parameters
 
779
    headerValue = buf.get();
 
780
 
 
781
  } else {
 
782
    // Extract value to left of parameters
 
783
    buf.Left(headerValue, semicolonOffset);
 
784
  }
 
785
 
 
786
  // Trim leading and trailing spaces in header value
 
787
  headerValue.Trim(" ");
 
788
 
 
789
  if (headerKey.Equals("content-type")) {
 
790
    mContentType = headerValue;
 
791
 
 
792
    DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentType=%s\n",
 
793
               mContentType.get()));
 
794
 
 
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);
 
804
 
 
805
      if (charset)
 
806
        mContentCharset = charset;
 
807
 
 
808
      if (boundary)
 
809
        mContentBoundary = boundary;
 
810
 
 
811
      if (protocol)
 
812
        mContentProtocol = protocol;
 
813
 
 
814
      if (micalg)
 
815
        mContentMicalg = micalg;
 
816
 
 
817
      PR_FREEIF(charset);
 
818
      PR_FREEIF(boundary);
 
819
      PR_FREEIF(protocol);
 
820
      PR_FREEIF(micalg);
 
821
 
 
822
      DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentCharset=%s\n",
 
823
                 mContentCharset.get()));
 
824
 
 
825
      DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentBoundary=%s\n",
 
826
                 mContentBoundary.get()));
 
827
 
 
828
      DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentProtocol=%s\n",
 
829
                 mContentProtocol.get()));
 
830
 
 
831
      DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentMicalg=%s\n",
 
832
                 mContentMicalg.get()));
 
833
    }
 
834
 
 
835
  } else if (headerKey.Equals("content-transfer-encoding")) {
 
836
    mContentEncoding = buf;
 
837
    ToLowerCase(mContentEncoding);
 
838
 
 
839
    DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentEncoding=%s\n",
 
840
               mContentEncoding.get()));
 
841
 
 
842
  } else if (headerKey.Equals("content-disposition")) {
 
843
    mContentDisposition = buf;
 
844
 
 
845
    DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentDisposition=%s\n",
 
846
               mContentDisposition.get()));
 
847
 
 
848
  } else if (headerKey.Equals("content-length")) {
 
849
    PRInt32 status;
 
850
    PRInt32 value = headerValue.ToInteger(&status);
 
851
 
 
852
    if (NS_SUCCEEDED((nsresult) status))
 
853
      mContentLength = value;
 
854
 
 
855
    DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContenLengtht=%d\n",
 
856
               mContentLength));
 
857
  }
 
858
 
 
859
  return;
 
860
}
 
861
 
 
862
 
 
863
///////////////////////////////////////////////////////////////////////////////
 
864
// nsIInputStream methods
 
865
///////////////////////////////////////////////////////////////////////////////
 
866
 
 
867
NS_IMETHODIMP
 
868
nsEnigMimeListener::Available(PRUint32* _retval)
 
869
{
 
870
  if (!_retval)
 
871
    return NS_ERROR_NULL_POINTER;
 
872
 
 
873
  *_retval = (mStreamLength > mStreamOffset) ?
 
874
              mStreamLength - mStreamOffset : 0;
 
875
 
 
876
  DEBUG_LOG(("nsEnigMimeListener::Available: (%p) %d\n", this, *_retval));
 
877
 
 
878
  return NS_OK;
 
879
}
 
880
 
 
881
NS_IMETHODIMP
 
882
nsEnigMimeListener::Read(char* buf, PRUint32 count,
 
883
                         PRUint32 *readCount)
 
884
{
 
885
  DEBUG_LOG(("nsEnigMimeListener::Read: (%p) %d\n", this, count));
 
886
 
 
887
  if (!buf || !readCount)
 
888
    return NS_ERROR_NULL_POINTER;
 
889
 
 
890
  PRInt32 avail = (mStreamLength > mStreamOffset) ?
 
891
                   mStreamLength - mStreamOffset : 0;
 
892
 
 
893
  *readCount = ((PRUint32) avail > count) ? count : avail;
 
894
 
 
895
  if (*readCount) {
 
896
    memcpy(buf, mStreamBuf+mStreamOffset, *readCount);
 
897
    mStreamOffset += *readCount;
 
898
  }
 
899
 
 
900
  if (mStreamOffset >= mStreamLength) {
 
901
    Close();
 
902
  }
 
903
 
 
904
  return NS_OK;
 
905
}
 
906
 
 
907
NS_IMETHODIMP
 
908
nsEnigMimeListener::ReadSegments(nsWriteSegmentFun writer,
 
909
                                 void * aClosure, PRUint32 count,
 
910
                                 PRUint32 *readCount)
 
911
{
 
912
  DEBUG_LOG(("nsEnigMimeListener::ReadSegments: %d\n", count));
 
913
 
 
914
  if (!readCount)
 
915
    return NS_ERROR_NULL_POINTER;
 
916
 
 
917
  PRInt32 avail = (mStreamLength > mStreamOffset) ?
 
918
                   mStreamLength - mStreamOffset : 0;
 
919
 
 
920
  PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
 
921
 
 
922
  if (!readyCount) {
 
923
    *readCount = 0;
 
924
 
 
925
  } else {
 
926
    nsresult rv = writer((nsIInputStream*)(this),
 
927
                         aClosure, mStreamBuf+mStreamOffset,
 
928
                         mStreamOffset, readyCount, readCount);
 
929
    if (NS_FAILED(rv))
 
930
      return rv;
 
931
 
 
932
    mStreamOffset += *readCount;
 
933
  }
 
934
 
 
935
  if (mStreamOffset >= mStreamLength) {
 
936
    Close();
 
937
  }
 
938
 
 
939
  return NS_OK;
 
940
}
 
941
 
 
942
NS_IMETHODIMP
 
943
nsEnigMimeListener::IsNonBlocking(PRBool *aNonBlocking)
 
944
{
 
945
  DEBUG_LOG(("nsEnigMimeListener::IsNonBlocking: \n"));
 
946
 
 
947
  *aNonBlocking = PR_TRUE;
 
948
  return NS_OK;
 
949
}
 
950
 
 
951
NS_IMETHODIMP
 
952
nsEnigMimeListener::GetSubPartTreatment(PRBool* aSubPartTreatment)
 
953
{
 
954
  *aSubPartTreatment = mSubPartTreatment;
 
955
  return NS_OK;
 
956
}
 
957
 
 
958
NS_IMETHODIMP
 
959
nsEnigMimeListener::SetSubPartTreatment(PRBool aSubPartTreatment)
 
960
{
 
961
  DEBUG_LOG(("nsEnigMimeListener::SetSubPartTreatment: %d\n", aSubPartTreatment));
 
962
 
 
963
  mSubPartTreatment = aSubPartTreatment;
 
964
  return NS_OK;
 
965
}
 
966
 
 
967
NS_IMETHODIMP
 
968
nsEnigMimeListener::Close()
 
969
{
 
970
  DEBUG_LOG(("nsEnigMimeListener::Close: (%p)\n", this));
 
971
  mStreamBuf = nsnull;
 
972
  mStreamOffset = 0;
 
973
  mStreamLength = 0;
 
974
  return NS_OK;
 
975
}