Back to index

enigmail  1.4.3
nsEnigMimeListener.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public
00005  * License Version 1.1 (the "MPL"); you may not use this file
00006  * except in compliance with the MPL. You may obtain a copy of
00007  * the MPL at http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the MPL is distributed on an "AS
00010  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
00011  * implied. See the MPL for the specific language governing
00012  * rights and limitations under the MPL.
00013  *
00014  * The Original Code is Enigmail.
00015  *
00016  * The Initial Developer of the Original Code is Ramalingam Saravanan.
00017  * Portions created by Ramalingam Saravanan <sarava@sarava.net> are
00018  * Copyright (C) 2002 Ramalingam Saravanan. All Rights Reserved.
00019  *
00020  * Contributor(s):
00021  * Patrick Brunschwig <patrick@mozilla-enigmail.org>
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  * ***** END LICENSE BLOCK ***** */
00035 
00036 // Logging of debug output
00037 // The following define statement should occur before any include statements
00038 #define FORCE_PR_LOG       /* Allow logging even in release build */
00039 
00040 #include "enigmail.h"
00041 #include "prlog.h"
00042 #include "nsCOMPtr.h"
00043 #include "nsIInputStream.h"
00044 #include "nsIThread.h"
00045 #include "nsStringAPI.h"
00046 #include "nsNetUtil.h"
00047 #include "mimehdrs2.h"
00048 #include "nsMimeTypes.h"
00049 #include "nsMailHeaders.h"
00050 
00051 #include "nsEnigMimeListener.h"
00052 
00053 #ifdef PR_LOGGING
00054 PRLogModuleInfo* gEnigMimeListenerLog = NULL;
00055 #endif
00056 
00057 #define ERROR_LOG(args)    PR_LOG(gEnigMimeListenerLog,PR_LOG_ERROR,args)
00058 #define WARNING_LOG(args)  PR_LOG(gEnigMimeListenerLog,PR_LOG_WARNING,args)
00059 #define DEBUG_LOG(args)    PR_LOG(gEnigMimeListenerLog,PR_LOG_DEBUG,args)
00060 
00061 #define NS_PIPE_CONSOLE_BUFFER_SIZE   (1024)
00062 
00063 static const PRUint32 kCharMax = 1024;
00064 
00065 #define MK_MIME_ERROR_WRITING_FILE -1
00066 
00068 
00069 // nsEnigMimeListener implementation
00070 
00071 // nsISupports implementation
00072 NS_IMPL_THREADSAFE_ISUPPORTS4(nsEnigMimeListener,
00073                               nsIEnigMimeListener,
00074                               nsIRequestObserver,
00075                               nsIStreamListener,
00076                               nsIInputStream)
00077 
00078 
00079 // nsEnigMimeListener implementation
00080 nsEnigMimeListener::nsEnigMimeListener()
00081   : mInitialized(PR_FALSE),
00082     mRequestStarted(PR_FALSE),
00083     mSkipHeaders(PR_FALSE),
00084     mSkipBody(PR_FALSE),
00085 
00086     mContentType(""),
00087     mContentCharset(""),
00088     mContentBoundary(""),
00089     mContentProtocol(""),
00090     mContentMicalg(""),
00091 
00092     mContentEncoding(""),
00093     mContentDisposition(""),
00094     mContentLength(-1),
00095 
00096     mDecodeContent(PR_FALSE),
00097     mDecoderData(nsnull),
00098 
00099     mLinebreak(""),
00100     mHeaders(""),
00101     mDataStr(""),
00102     mHeaderSearchCounter(0),
00103 
00104     mHeadersFinalCR(PR_FALSE),
00105     mHeadersLinebreak(2),
00106 
00107     mMaxHeaderBytes(0),
00108     mDataOffset(0),
00109 
00110     mStreamBuf(nsnull),
00111     mStreamOffset(0),
00112     mStreamLength(0),
00113     mSubPartTreatment(PR_FALSE),
00114 
00115     mListener(nsnull),
00116     mContext(nsnull)
00117 {
00118     NS_INIT_ISUPPORTS();
00119 
00120 #ifdef PR_LOGGING
00121   if (gEnigMimeListenerLog == nsnull) {
00122     gEnigMimeListenerLog = PR_NewLogModule("nsEnigMimeListener");
00123   }
00124 #endif
00125 
00126 #ifdef FORCE_PR_LOG
00127   nsresult rv;
00128   nsCOMPtr<nsIThread> myThread;
00129   rv = ENIG_GET_THREAD(myThread);
00130   DEBUG_LOG(("nsEnigMimeListener:: <<<<<<<<< CTOR(%p): myThread=%p\n",
00131          this, myThread.get()));
00132 #endif
00133 }
00134 
00135 
00136 nsEnigMimeListener::~nsEnigMimeListener()
00137 {
00138   nsresult rv;
00139 #ifdef FORCE_PR_LOG
00140   nsCOMPtr<nsIThread> myThread;
00141   rv = ENIG_GET_THREAD(myThread);
00142   DEBUG_LOG(("nsEnigMimeListener:: >>>>>>>>> DTOR(%p): myThread=%p\n",
00143          this, myThread.get()));
00144 #endif
00145 
00146   if (mDecoderData) {
00147     // Clear decoder buffer
00148     MimeDecoderDestroy(mDecoderData, PR_FALSE);
00149     mDecoderData = nsnull;
00150   }
00151 
00152   // Release owning refs
00153   mListener = nsnull;
00154   mContext = nsnull;
00155 }
00156 
00157 
00159 // nsIEnigMimeListener methods
00161 
00162 NS_IMETHODIMP
00163 nsEnigMimeListener::Init(nsIStreamListener* listener, nsISupports* ctxt,
00164                          PRUint32 maxHeaderBytes, EMBool skipHeaders,
00165                          EMBool skipBody, EMBool decodeContent)
00166 {
00167   DEBUG_LOG(("nsEnigMimeListener::Init: (%p) %d, %d, %d, %d\n", this,
00168              maxHeaderBytes, skipHeaders, skipBody, decodeContent));
00169 
00170   if (!listener)
00171     return NS_ERROR_NULL_POINTER;
00172 
00173   mListener = listener;
00174   mContext = ctxt;
00175 
00176   mMaxHeaderBytes = maxHeaderBytes;
00177 
00178   mSkipHeaders = skipHeaders;
00179   mSkipBody = skipBody;
00180   mDecodeContent = decodeContent;
00181 
00182   // There is implicitly a newline preceding the first character
00183   mHeadersLinebreak = 2;
00184   mHeadersFinalCR = PR_FALSE;
00185 
00186   mInitialized = PR_TRUE;
00187 
00188   return NS_OK;
00189 }
00190 
00191 
00192 NS_IMETHODIMP
00193 nsEnigMimeListener::Write(const char* buf, PRUint32 count,
00194                           nsIRequest* aRequest, nsISupports* aContext)
00195 {
00196   nsresult rv;
00197 
00198   DEBUG_LOG(("nsEnigMimeListener::Write: (%p) %d\n", this, count));
00199 
00200   if (mRequestStarted)
00201     return Transmit(buf, count, aRequest, aContext);
00202 
00203   // Search for headers
00204   EMBool startingRequest = HeaderSearch(buf, count);
00205   if (!startingRequest)
00206     return NS_OK;
00207 
00208   rv = StartRequest(aRequest, aContext);
00209   if (NS_FAILED(rv))
00210     return rv;
00211 
00212   return NS_OK;
00213 }
00214 
00215 static nsresult
00216 EnigMimeListener_write(const char *buf, PRInt32 size, void *closure)
00217 {
00218   DEBUG_LOG(("nsEnigMimeListener::EnigMimeListener_write: (%p) %d\n", closure, size));
00219 
00220   if (!closure)
00221     return NS_ERROR_FAILURE;
00222 
00223   nsEnigMimeListener* enigMimeListener = (nsEnigMimeListener *) closure;
00224 
00225   return enigMimeListener->SendStream(buf, size, nsnull, nsnull);
00226 }
00227 
00228 
00229 NS_METHOD
00230 nsEnigMimeListener::Transmit(const char* buf, PRUint32 count,
00231                              nsIRequest* aRequest, nsISupports* aContext)
00232 {
00233   DEBUG_LOG(("nsEnigMimeListener::Transmit: (%p) %d\n", this, count));
00234 
00235   if (!mDecoderData) {
00236     return SendStream(buf, count, aRequest, aContext);
00237   }
00238 
00239   // Decode data before transmitting to listener
00240   int status = MimeDecoderWrite(mDecoderData, buf, count);
00241 
00242   return (status == 0) ? NS_OK : NS_ERROR_FAILURE;
00243 }
00244 
00245 
00246 NS_METHOD
00247 nsEnigMimeListener::SendStream(const char* buf, PRUint32 count,
00248                                nsIRequest* aRequest, nsISupports* aContext)
00249 {
00250   nsresult rv;
00251 
00252   DEBUG_LOG(("nsEnigMimeListener::SendStream: (%p) %d\n", this, count));
00253 
00254   if (!mListener)
00255     return NS_OK;
00256 
00257   // Transmit data to listener
00258   mStreamBuf = buf;
00259   mStreamOffset = 0;
00260   mStreamLength = count;
00261 
00262   rv = mListener->OnDataAvailable(aRequest,
00263                                   mContext ? mContext.get() : aContext,
00264                                   (nsIInputStream*)(this),
00265                                   0, count);
00266   Close();
00267 
00268   return rv;
00269 }
00270 
00271 
00272 NS_IMETHODIMP
00273 nsEnigMimeListener::GetHeaders(nsACString &aHeaders)
00274 {
00275   aHeaders = mHeaders;
00276   DEBUG_LOG(("nsEnigMimeListener::GetHeaders: %d\n", mHeaders.Length()));
00277   return NS_OK;
00278 }
00279 
00280 NS_IMETHODIMP
00281 nsEnigMimeListener::GetLinebreak(nsACString &aLinebreak)
00282 {
00283   aLinebreak = mLinebreak;
00284   DEBUG_LOG(("nsEnigMimeListener::GetLinebreak: %d\n", mLinebreak.Length()));
00285   return NS_OK;
00286 }
00287 
00288 NS_IMETHODIMP
00289 nsEnigMimeListener::GetContentType(nsACString &aContentType)
00290 {
00291   aContentType = mContentType;
00292   DEBUG_LOG(("nsEnigMimeListener::GetContentType: %s\n", mContentType.get()));
00293   return NS_OK;
00294 }
00295 
00296 NS_IMETHODIMP
00297 nsEnigMimeListener::GetContentCharset(nsACString &aContentCharset)
00298 {
00299   aContentCharset = mContentCharset;
00300   DEBUG_LOG(("nsEnigMimeListener::GetContentCharset: %s\n", mContentCharset.get()));
00301   return NS_OK;
00302 }
00303 
00304 NS_IMETHODIMP
00305 nsEnigMimeListener::GetContentBoundary(nsACString &aContentBoundary)
00306 {
00307   aContentBoundary = mContentBoundary;
00308   DEBUG_LOG(("nsEnigMimeListener::GetContentBoundary: %s\n", mContentBoundary.get()));
00309   return NS_OK;
00310 }
00311 
00312 NS_IMETHODIMP
00313 nsEnigMimeListener::GetContentProtocol(nsACString &aContentProtocol)
00314 {
00315   aContentProtocol = mContentProtocol;
00316   DEBUG_LOG(("nsEnigMimeListener::GetContentProtocol: %s\n", mContentProtocol.get()));
00317   return NS_OK;
00318 }
00319 
00320 NS_IMETHODIMP
00321 nsEnigMimeListener::GetContentMicalg(nsACString &aContentMicalg)
00322 {
00323   aContentMicalg = mContentMicalg;
00324   DEBUG_LOG(("nsEnigMimeListener::GetContentMicalg: %s\n", mContentMicalg.get()));
00325   return NS_OK;
00326 }
00327 
00328 NS_IMETHODIMP
00329 nsEnigMimeListener::GetContentEncoding(nsACString &aContentEncoding)
00330 {
00331   aContentEncoding = mContentEncoding;
00332   DEBUG_LOG(("nsEnigMimeListener::GetContentEncoding: %s\n", mContentEncoding.get()));
00333   return NS_OK;
00334 }
00335 
00336 NS_IMETHODIMP
00337 nsEnigMimeListener::GetContentDisposition(nsACString &aContentDisposition)
00338 {
00339   aContentDisposition = mContentDisposition;
00340   DEBUG_LOG(("nsEnigMimeListener::GetContentDisposition: %s\n", mContentDisposition.get()));
00341   return NS_OK;
00342 }
00343 
00344 NS_IMETHODIMP
00345 nsEnigMimeListener::GetContentLength(PRInt32 *aContentLength)
00346 {
00347   DEBUG_LOG(("nsEnigMimeListener::GetContentLength: \n"));
00348   *aContentLength = mContentLength;
00349   return NS_OK;
00350 }
00351 
00353 // nsIRequestObserver methods
00355 
00356 NS_IMETHODIMP
00357 nsEnigMimeListener::OnStartRequest(nsIRequest *aRequest,
00358                                    nsISupports *aContext)
00359 {
00360   DEBUG_LOG(("nsEnigMimeListener::OnStartRequest: (%p)\n", this));
00361 
00362   if (!mInitialized)
00363     return NS_ERROR_NOT_INITIALIZED;
00364 
00365   return NS_OK;
00366 }
00367 
00368 NS_IMETHODIMP
00369 nsEnigMimeListener::OnStopRequest(nsIRequest* aRequest,
00370                                   nsISupports* aContext,
00371                                   nsresult aStatus)
00372 {
00373   nsresult rv = NS_OK;
00374 
00375   DEBUG_LOG(("nsEnigMimeListener::OnStopRequest: (%p)\n", this));
00376 
00377   // Ensure that OnStopRequest call chain does not break by failing softly
00378 
00379   if (!mRequestStarted) {
00380 
00381     if (mHeadersFinalCR) {
00382       // Handle special case of terminating CR with no content
00383       mHeadersFinalCR = PR_FALSE;
00384 
00385       mLinebreak = "\r";
00386       mHeaders = mDataStr;
00387 
00388       if (mSkipHeaders) {
00389         // Skip headers
00390         mDataStr = "";
00391       }
00392     }
00393 
00394     rv = StartRequest(aRequest, aContext);
00395     if (NS_FAILED(rv))
00396       aStatus = NS_BINDING_ABORTED;
00397   }
00398 
00399   if (mDecoderData) {
00400     // Clear decoder buffer
00401     MimeDecoderDestroy(mDecoderData, PR_FALSE);
00402     mDecoderData = nsnull;
00403   }
00404 
00405   if (mListener) {
00406     rv = mListener->OnStopRequest(aRequest,
00407                                   mContext ? mContext.get() : aContext,
00408                                   aStatus);
00409     if (NS_FAILED(rv))
00410       aStatus = NS_BINDING_ABORTED;
00411   }
00412 
00413   // Release owning refs
00414   mListener = nsnull;
00415   mContext = nsnull;
00416 
00417   return (aStatus == NS_BINDING_ABORTED) ? NS_ERROR_FAILURE : NS_OK;
00418 }
00419 
00421 // nsIStreamListener method
00423 
00424 NS_IMETHODIMP
00425 nsEnigMimeListener::OnDataAvailable(nsIRequest* aRequest,
00426                                     nsISupports* aContext,
00427                                     nsIInputStream *aInputStream,
00428                                     PRUint32 aSourceOffset,
00429                                     PRUint32 aLength)
00430 {
00431   nsresult rv = NS_OK;
00432 
00433   DEBUG_LOG(("nsEnigMimeListener::OnDataAvailable: (%p) %d\n", this, aLength));
00434 
00435   if (!mInitialized)
00436     return NS_ERROR_NOT_INITIALIZED;
00437 
00438   char buf[kCharMax];
00439   PRUint32 readCount, readMax;
00440 
00441   while ((aLength > 0) && (!mRequestStarted || mDecoderData) ) {
00442     // Searching for headers or decoding content
00443 
00444     readMax = (aLength < kCharMax) ? aLength : kCharMax;
00445     rv = aInputStream->Read((char *) buf, readMax, &readCount);
00446     if (NS_FAILED(rv)){
00447       ERROR_LOG(("nsEnigMimeListener::OnDataAvailable: Error in reading from input stream, %x\n", rv));
00448       return rv;
00449     }
00450 
00451     if (readCount <= 0)
00452       break;
00453 
00454     aLength -= readCount;
00455     aSourceOffset += readCount;
00456 
00457     rv = Write(buf, readCount, aRequest, aContext);
00458     if (NS_FAILED(rv))
00459       return rv;
00460   }
00461 
00462   // Not searching for headers and not decoding content
00463   if (!mSkipBody && (aLength > 0) && mListener) {
00464     // Transmit body data unread
00465     rv = mListener->OnDataAvailable(aRequest,
00466                                     mContext ? mContext.get() : aContext,
00467                                     aInputStream, mDataOffset, aLength);
00468     mDataOffset += aLength;
00469 
00470     if (NS_FAILED(rv))
00471       return rv;
00472   }
00473 
00474   return NS_OK;
00475 }
00476 
00477 
00478 NS_IMETHODIMP
00479 nsEnigMimeListener::StartRequest(nsIRequest* aRequest, nsISupports* aContext)
00480 {
00481   nsresult rv;
00482 
00483   DEBUG_LOG(("nsEnigMimeListener::StartRequest: (%p)\n", this));
00484 
00485   if (!mHeaders.IsEmpty()) {
00486     // Try to parse headers
00487     ParseMimeHeaders(mHeaders.get(), mHeaders.Length());
00488   }
00489 
00490   if (mListener) {
00491     rv = mListener->OnStartRequest(aRequest,
00492                                    mContext ? mContext.get() : aContext);
00493     if (NS_FAILED(rv))
00494       return rv;
00495   }
00496 
00497   mRequestStarted = PR_TRUE;
00498 
00499   if (mHeaders.IsEmpty() && mSkipBody) {
00500     // No headers terminated and skipping body; so discard whatever we have
00501     mDataStr = "";
00502   }
00503 
00504   if (!mDataStr.IsEmpty()) {
00505     // Transmit header/body data already in buffer
00506     nsCAutoString temStr( mDataStr );
00507 
00508     mDataOffset += mDataStr.Length();
00509     mDataStr = "";
00510 
00511     rv = Transmit(temStr.get(), temStr.Length(), aRequest, aContext);
00512     if (NS_FAILED(rv))
00513       return rv;
00514   }
00515 
00516   return NS_OK;
00517 }
00518 
00519 
00520 EMBool
00521 nsEnigMimeListener::HeaderSearch(const char* buf, PRUint32 count)
00522 {
00523   DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: (%p) count=%d\n", this, count));
00524 
00525   mHeaderSearchCounter++;
00526 
00527   if (mMaxHeaderBytes <= 0) {
00528     // Not looking for MIME headers; start request immediately
00529     return PR_TRUE;
00530   }
00531 
00532   if (!count)
00533     return PR_FALSE;
00534 
00535   PRUint32 bytesAvailable = mMaxHeaderBytes - mDataStr.Length();
00536   NS_ASSERTION(bytesAvailable > 0, "bytesAvailable <= 0");
00537 
00538   EMBool lastSegment = (bytesAvailable <= count);
00539 
00540   PRUint32 scanLen = lastSegment ? bytesAvailable : count;
00541 
00542   EMBool headersFound = PR_FALSE;
00543   PRUint32 offset = 0;
00544   PRUint32 startOffset = 0;
00545   PRUint32 j = 0;
00546   char ch;
00547   if (mSubPartTreatment) {
00548     // FIXME:
00549     // this is a HACK necessary because Mozilla does not deliver
00550     // a subpart starting with its headers (so we get the
00551     // part on a higher level and sort out things manually!)
00552     // there is (so far) no way to get the headers of an
00553     // arbitrary message mime part
00554     DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: subparts treatment\n"));
00555     ch='\n';
00556     while(j<scanLen-3) {
00557       if (((ch=='\n') || (ch=='\r')) &&
00558           (buf[j]=='-') &&
00559           (buf[j+1]=='-') &&
00560           (buf[j+2]!='\n') &&
00561           (buf[j+2]!='\r'))
00562       {
00563           startOffset = j;
00564           DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: startOffset=%d\n",startOffset));
00565           break;
00566       }
00567       ch=buf[j];
00568       j++;
00569     }
00570 
00571     // set j=startOffset needed if startOffset == 0!
00572     j=startOffset;
00573 /*
00574     // Solution for how to do it, if the content-type info
00575     // would be available
00576     nsCAutoString cType("Content-Type: multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=\"J2SCkAp4GZ/dPZZf\"\n\n");
00577     mDataStr.Append(cType.get(), cType.Length());
00578     mHeaders = cType;
00579     if (mSkipHeaders)
00580       mDataStr = "";
00581     if (!mSkipBody)
00582       mDataStr.Append(buf, count);
00583 
00584     mHeadersLinebreak = 0;
00585     mLinebreak = "\n";
00586 */
00587     mSubPartTreatment = PR_FALSE;
00588     // return PR_TRUE;
00589   }
00590 
00591   while (j<scanLen) {
00592     ch = buf[j];
00593 
00594     if (mHeadersFinalCR) {
00595       // End-of-headers found
00596       mHeadersFinalCR = PR_FALSE;
00597 
00598       if (ch == '\n') {
00599         offset = j+1;
00600         mLinebreak = "\r\n";
00601         DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CRLF"));
00602 
00603       } else {
00604         offset = j;
00605         mLinebreak = "\r";
00606         DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final CR"));
00607       }
00608 
00609       headersFound = PR_TRUE;
00610       break;
00611 
00612     }
00613 
00614     if (ch == '\n') {
00615 
00616       if (mHeadersLinebreak == 2) {
00617         // End-of-headers found
00618         headersFound = PR_TRUE;
00619 
00620         offset = j+1;
00621         mLinebreak = "\n";
00622         DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: Found final LF"));
00623         break;
00624       }
00625 
00626       mHeadersLinebreak = 2;
00627 
00628     } else if (ch == '\r') {
00629 
00630       if (mHeadersLinebreak > 0) {
00631         // Final CR
00632         mHeadersFinalCR = PR_TRUE;
00633       } else {
00634         mHeadersLinebreak = 1;
00635       }
00636 
00637     } else {
00638       mHeadersLinebreak = 0;
00639     }
00640 
00641     j++;
00642   }
00643 
00644   DEBUG_LOG(("nsEnigMimeListener::HeaderSearch: offset=%d\n", offset));
00645 
00646   if (headersFound) {
00647     // Copy headers out of stream buffer
00648     if (offset > 0)
00649       mDataStr.Append(buf+startOffset, offset-startOffset);
00650 
00651     mHeaders = mDataStr;
00652 
00653     if (mSkipHeaders) {
00654       // Skip headers
00655       mDataStr = "";
00656     }
00657 
00658     if (!mSkipBody && (offset < count)) {
00659       // Copy remaining data into stream buffer
00660      mDataStr.Append(buf+offset, count-offset);
00661     }
00662 
00663   } else if (!lastSegment) {
00664     // Save headers data
00665     mDataStr.Append(buf, count);
00666   }
00667 
00668   return headersFound || lastSegment;
00669 }
00670 
00671 static void
00672 __ReplaceCSubstring (nsACString &string, const char* replace, const char* with)
00673 {
00674        PRInt32 i = string.Find (replace);
00675        while ( i >= 0 ) {
00676        string.Replace (i, strlen (replace), with);
00677        i = string.Find (replace);
00678   }
00679 }
00680 
00681 static void
00682 __ReplaceCChar (nsACString &string, const char replace, const char with)
00683 {
00684        PRInt32 i = string.FindChar (replace);
00685   while (i >= 0 ) {
00686          string.Replace (i, 1, (const char*) &with, 1);
00687          i = string.FindChar (replace);
00688        }
00689 }
00690 
00691 void
00692 nsEnigMimeListener::ParseMimeHeaders(const char* mimeHeaders, PRUint32 count)
00693 {
00694   DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders, count=%d\n", count));
00695 
00696   // Copy headers string
00697   nsCAutoString headers(mimeHeaders, count);
00698 
00699   // Replace CRLF with just LF
00700   __ReplaceCSubstring(headers, "\r\n", "\n");
00701 
00702   // Replace CR with LF (for MAC-style line endings)
00703   __ReplaceCChar(headers, '\r', '\n');
00704 
00705   // Eliminate all leading whitespace (including linefeeds)
00706   headers.Trim(" \t\n", PR_TRUE, PR_FALSE);
00707 
00708   if (headers.Length() <= 3) {
00709     // No headers to parse
00710     return;
00711   }
00712 
00713   // Handle continuation of MIME headers, i.e., newline followed by whitespace
00714   __ReplaceCSubstring(headers, "\n ",  " ");
00715   __ReplaceCSubstring(headers, "\n\t", "\t");
00716 
00717   //DEBUG_LOG(("nsEnigMimeListener::ParseMimeHeaders: headers='%s'\n", headers.get()));
00718 
00719   PRUint32 offset = 0;
00720   while (offset < headers.Length()) {
00721     PRInt32 lineEnd = headers.FindChar('\n', offset);
00722 
00723     if (lineEnd < 0) {
00724       // Header line terminator not found
00725       NS_NOTREACHED("lineEnd == kNotFound");
00726       return;
00727     }
00728 
00729     // Normal exit if empty header line
00730     if (lineEnd == (int)offset)
00731       break;
00732 
00733     // Parse header line
00734     ParseHeader((headers.get())+offset, lineEnd - offset);
00735 
00736     offset = lineEnd+1;
00737   }
00738 
00739   if (mDecodeContent) {
00740     // Decode data
00741     if (mContentEncoding.Equals("base64", CaseInsensitiveCompare)) {
00742 
00743       mDecoderData = MimeB64DecoderInit(EnigMimeListener_write, (void*) this);
00744 
00745     } else if (mContentEncoding.Equals("quoted-printable", CaseInsensitiveCompare)) {
00746 
00747       mDecoderData = MimeQPDecoderInit(EnigMimeListener_write, (void*) this);
00748     }
00749   }
00750   return;
00751 }
00752 
00753 void
00754 nsEnigMimeListener::ParseHeader(const char* header, PRUint32 count)
00755 {
00756 
00757   //DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", header));
00758 
00759   if (!header || (count <= 0) )
00760     return;
00761 
00762   // Create header string
00763   nsCAutoString headerStr(header, count);
00764 
00765   //DEBUG_LOG(("nsEnigMimeListener::ParseHeader: header='%s'\n", headerStr.get()));
00766   PRInt32 colonOffset;
00767   colonOffset = headerStr.FindChar(':');
00768   if (colonOffset < 0)
00769     return;
00770 
00771   // Null header key not allowed
00772   if (colonOffset == 0)
00773     return;
00774 
00775   // Extract header key (not case-sensitive)
00776   nsCAutoString headerKey = (nsCString) nsDependentCSubstring (headerStr, 0, colonOffset);
00777   ToLowerCase(headerKey);
00778 
00779 
00780   // Extract header value, trimming leading/trailing whitespace
00781   nsCAutoString buf = (nsCString) nsDependentCSubstring (headerStr, colonOffset+1, headerStr.Length() - colonOffset);
00782   buf.Trim(" ", PR_TRUE, PR_TRUE);
00783 
00784   //DEBUG_LOG(("nsEnigMimeListener::ParseHeader: '%s': %s\n", headerKey.get(), buf.get()));
00785 
00786   PRInt32 semicolonOffset = buf.FindChar(';');
00787 
00788   nsCString headerValue;
00789   if (semicolonOffset < 0) {
00790     // No parameters
00791     headerValue = ((nsCString)buf).get();
00792 
00793   } else {
00794     // Extract value to left of parameters
00795     headerValue = nsDependentCSubstring (buf, 0, semicolonOffset);
00796   }
00797 
00798   // Trim leading and trailing spaces in header value
00799   headerValue.Trim(" ", PR_TRUE, PR_TRUE);
00800 
00801   if (headerKey.Equals("content-type")) {
00802     mContentType = headerValue;
00803 
00804     DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentType=%s\n",
00805                mContentType.get()));
00806 
00807     if (!buf.IsEmpty()) {
00808       char *charset  = MimeHeaders_get_parameter(buf.get(),
00809                               HEADER_PARM_CHARSET, NULL, NULL);
00810       char *boundary = MimeHeaders_get_parameter(buf.get(),
00811                               HEADER_PARM_BOUNDARY, NULL, NULL);
00812       char *protocol = MimeHeaders_get_parameter(buf.get(),
00813                               PARAM_PROTOCOL, NULL, NULL);
00814       char *micalg   = MimeHeaders_get_parameter(buf.get(),
00815                                PARAM_MICALG, NULL, NULL);
00816 
00817       if (charset)
00818         mContentCharset = charset;
00819 
00820       if (boundary)
00821         mContentBoundary = boundary;
00822 
00823       if (protocol)
00824         mContentProtocol = protocol;
00825 
00826       if (micalg)
00827         mContentMicalg = micalg;
00828 
00829       PR_FREEIF(charset);
00830       PR_FREEIF(boundary);
00831       PR_FREEIF(protocol);
00832       PR_FREEIF(micalg);
00833 
00834       DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentCharset=%s\n",
00835                  mContentCharset.get()));
00836 
00837       DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentBoundary=%s\n",
00838                  mContentBoundary.get()));
00839 
00840       DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentProtocol=%s\n",
00841                  mContentProtocol.get()));
00842 
00843       DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentMicalg=%s\n",
00844                  mContentMicalg.get()));
00845     }
00846 
00847   } else if (headerKey.Equals("content-transfer-encoding")) {
00848     mContentEncoding = buf;
00849     ToLowerCase(mContentEncoding);
00850 
00851     DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentEncoding=%s\n",
00852                mContentEncoding.get()));
00853 
00854   } else if (headerKey.Equals("content-disposition")) {
00855     mContentDisposition = buf;
00856 
00857     DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContentDisposition=%s\n",
00858                mContentDisposition.get()));
00859 
00860   } else if (headerKey.Equals("content-length")) {
00861     nsresult status;
00862     PRInt32 value = headerValue.ToInteger(&status);
00863 
00864     if (NS_SUCCEEDED(status))
00865       mContentLength = value;
00866 
00867     DEBUG_LOG(("nsEnigMimeListener::ParseHeader: ContenLengtht=%d\n",
00868                mContentLength));
00869   }
00870 
00871   return;
00872 }
00873 
00874 
00876 // nsIInputStream methods
00878 
00879 NS_IMETHODIMP
00880 nsEnigMimeListener::Available(PRUint32* _retval)
00881 {
00882   if (!_retval)
00883     return NS_ERROR_NULL_POINTER;
00884 
00885   *_retval = (mStreamLength > mStreamOffset) ?
00886               mStreamLength - mStreamOffset : 0;
00887 
00888   DEBUG_LOG(("nsEnigMimeListener::Available: (%p) %d\n", this, *_retval));
00889 
00890   return NS_OK;
00891 }
00892 
00893 NS_IMETHODIMP
00894 nsEnigMimeListener::Read(char* buf, PRUint32 count,
00895                          PRUint32 *readCount)
00896 {
00897   DEBUG_LOG(("nsEnigMimeListener::Read: (%p) %d\n", this, count));
00898 
00899   if (!buf || !readCount)
00900     return NS_ERROR_NULL_POINTER;
00901 
00902   PRInt32 avail = (mStreamLength > mStreamOffset) ?
00903                    mStreamLength - mStreamOffset : 0;
00904 
00905   *readCount = ((PRUint32) avail > count) ? count : avail;
00906 
00907   if (*readCount) {
00908     memcpy(buf, mStreamBuf+mStreamOffset, *readCount);
00909     mStreamOffset += *readCount;
00910   }
00911 
00912   if (mStreamOffset >= mStreamLength) {
00913     Close();
00914   }
00915 
00916   return NS_OK;
00917 }
00918 
00919 NS_IMETHODIMP
00920 nsEnigMimeListener::ReadSegments(nsWriteSegmentFun writer,
00921                                  void * aClosure, PRUint32 count,
00922                                  PRUint32 *readCount)
00923 {
00924   DEBUG_LOG(("nsEnigMimeListener::ReadSegments: %d\n", count));
00925 
00926   if (!readCount)
00927     return NS_ERROR_NULL_POINTER;
00928 
00929   PRInt32 avail = (mStreamLength > mStreamOffset) ?
00930                    mStreamLength - mStreamOffset : 0;
00931 
00932   PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
00933 
00934   if (!readyCount) {
00935     *readCount = 0;
00936 
00937   } else {
00938     nsresult rv = writer((nsIInputStream*)(this),
00939                          aClosure, mStreamBuf+mStreamOffset,
00940                          mStreamOffset, readyCount, readCount);
00941     if (NS_FAILED(rv))
00942       return rv;
00943 
00944     mStreamOffset += *readCount;
00945   }
00946 
00947   if (mStreamOffset >= mStreamLength) {
00948     Close();
00949   }
00950 
00951   return NS_OK;
00952 }
00953 
00954 NS_IMETHODIMP
00955 nsEnigMimeListener::IsNonBlocking(EMBool *aNonBlocking)
00956 {
00957   DEBUG_LOG(("nsEnigMimeListener::IsNonBlocking: \n"));
00958 
00959   *aNonBlocking = PR_TRUE;
00960   return NS_OK;
00961 }
00962 
00963 NS_IMETHODIMP
00964 nsEnigMimeListener::GetSubPartTreatment(EMBool* aSubPartTreatment)
00965 {
00966   *aSubPartTreatment = mSubPartTreatment;
00967   return NS_OK;
00968 }
00969 
00970 NS_IMETHODIMP
00971 nsEnigMimeListener::SetSubPartTreatment(EMBool aSubPartTreatment)
00972 {
00973   DEBUG_LOG(("nsEnigMimeListener::SetSubPartTreatment: %d\n", aSubPartTreatment));
00974 
00975   mSubPartTreatment = aSubPartTreatment;
00976   return NS_OK;
00977 }
00978 
00979 NS_IMETHODIMP
00980 nsEnigMimeListener::Close()
00981 {
00982   DEBUG_LOG(("nsEnigMimeListener::Close: (%p)\n", this));
00983   mStreamBuf = nsnull;
00984   mStreamOffset = 0;
00985   mStreamLength = 0;
00986   return NS_OK;
00987 }