Back to index

lightning-sunbird  0.9+nobinonly
nsUnknownDecoder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsUnknownDecoder.h"
00039 #include "nsIServiceManager.h"
00040 #include "nsIStreamConverterService.h"
00041 
00042 #include "nsIPipe.h"
00043 #include "nsIInputStream.h"
00044 #include "nsIOutputStream.h"
00045 #include "nsMimeTypes.h"
00046 #include "netCore.h"
00047 #include "nsXPIDLString.h"
00048 #include "nsIPrefService.h"
00049 #include "nsIPrefBranch.h"
00050 #include "nsICategoryManager.h"
00051 #include "nsISupportsPrimitives.h"
00052 #include "nsIContentSniffer.h"
00053 
00054 #include "nsCRT.h"
00055 
00056 #include "nsIMIMEService.h"
00057 
00058 #include "nsIViewSourceChannel.h"
00059 
00060 #include "prcpucfg.h" // To get IS_LITTLE_ENDIAN / IS_BIG_ENDIAN
00061 
00062 #define MAX_BUFFER_SIZE 1024
00063 
00064 #if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN
00065 #define LITTLE_TO_NATIVE16(x) ((((x) & 0xFF) << 8) | ((x) >> 8))
00066 #else
00067 #define LITTLE_TO_NATIVE16(x) x
00068 #endif
00069 
00070 nsUnknownDecoder::nsUnknownDecoder()
00071   : mBuffer(nsnull)
00072   , mBufferLen(0)
00073   , mRequireHTMLsuffix(PR_FALSE)
00074 {
00075   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
00076   if (prefs) {
00077     PRBool val;
00078     if (NS_SUCCEEDED(prefs->GetBoolPref("security.requireHTMLsuffix", &val)))
00079       mRequireHTMLsuffix = val;
00080   }
00081 }
00082 
00083 nsUnknownDecoder::~nsUnknownDecoder()
00084 {
00085   if (mBuffer) {
00086     delete [] mBuffer;
00087     mBuffer = nsnull;
00088   }
00089 }
00090 
00091 // ----
00092 //
00093 // nsISupports implementation...
00094 //
00095 // ----
00096 
00097 NS_IMPL_ADDREF(nsUnknownDecoder)
00098 NS_IMPL_RELEASE(nsUnknownDecoder)
00099 
00100 NS_INTERFACE_MAP_BEGIN(nsUnknownDecoder)
00101    NS_INTERFACE_MAP_ENTRY(nsIStreamConverter)
00102    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00103    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00104    NS_INTERFACE_MAP_ENTRY(nsIContentSniffer_MOZILLA_1_8_BRANCH)
00105    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
00106 NS_INTERFACE_MAP_END
00107 
00108 
00109 // ----
00110 //
00111 // nsIStreamConverter methods...
00112 //
00113 // ----
00114 
00115 NS_IMETHODIMP
00116 nsUnknownDecoder::Convert(nsIInputStream *aFromStream,
00117                           const char *aFromType,
00118                           const char *aToType,
00119                           nsISupports *aCtxt, 
00120                           nsIInputStream **aResultStream) 
00121 {
00122   return NS_ERROR_NOT_IMPLEMENTED;
00123 }
00124 
00125 NS_IMETHODIMP
00126 nsUnknownDecoder::AsyncConvertData(const char *aFromType, 
00127                                    const char *aToType,
00128                                    nsIStreamListener *aListener, 
00129                                    nsISupports *aCtxt)
00130 {
00131   NS_ASSERTION(aListener && aFromType && aToType, 
00132                "null pointer passed into multi mixed converter");
00133   // hook up our final listener. this guy gets the various On*() calls we want to throw
00134   // at him.
00135   //
00136   mNextListener = aListener;
00137   return (aListener) ? NS_OK : NS_ERROR_FAILURE;
00138 }
00139 
00140 // ----
00141 //
00142 // nsIStreamListener methods...
00143 //
00144 // ----
00145 
00146 NS_IMETHODIMP
00147 nsUnknownDecoder::OnDataAvailable(nsIRequest* request, 
00148                                   nsISupports *aCtxt,
00149                                   nsIInputStream *aStream, 
00150                                   PRUint32 aSourceOffset, 
00151                                   PRUint32 aCount)
00152 {
00153   nsresult rv = NS_OK;
00154 
00155   if (!mNextListener) return NS_ERROR_FAILURE;
00156 
00157   if (mContentType.IsEmpty()) {
00158     PRUint32 count, len;
00159 
00160     // If the buffer has not been allocated by now, just fail...
00161     if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY;
00162 
00163     //
00164     // Determine how much of the stream should be read to fill up the
00165     // sniffer buffer...
00166     //
00167     if (mBufferLen + aCount >= MAX_BUFFER_SIZE) {
00168       count = MAX_BUFFER_SIZE-mBufferLen;
00169     } else {
00170       count = aCount;
00171     }
00172 
00173     // Read the data into the buffer...
00174     rv = aStream->Read((mBuffer+mBufferLen), count, &len);
00175     if (NS_FAILED(rv)) return rv;
00176 
00177     mBufferLen += len;
00178     aCount     -= len;
00179 
00180     if (aCount) {
00181       //
00182       // Adjust the source offset...  The call to FireListenerNotifications(...)
00183       // will make the first OnDataAvailable(...) call with an offset of 0.
00184       // So, this offset needs to be adjusted to reflect that...
00185       //
00186       aSourceOffset += mBufferLen;
00187 
00188       DetermineContentType(request);
00189 
00190       NS_ASSERTION(!mContentType.IsEmpty(), 
00191                    "Content type should be known by now.");
00192       rv = FireListenerNotifications(request, aCtxt);
00193     }
00194   }
00195 
00196   // Must not fire ODA again if it failed once
00197   if (aCount && NS_SUCCEEDED(rv)) {
00198     NS_ASSERTION(!mContentType.IsEmpty(), 
00199                  "Content type should be known by now.");
00200 
00201     rv = mNextListener->OnDataAvailable(request, aCtxt, aStream, 
00202                                         aSourceOffset, aCount);
00203   }
00204 
00205   return rv;
00206 }
00207 
00208 // ----
00209 //
00210 // nsIRequestObserver methods...
00211 //
00212 // ----
00213 
00214 NS_IMETHODIMP
00215 nsUnknownDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt) 
00216 {
00217   nsresult rv = NS_OK;
00218 
00219   if (!mNextListener) return NS_ERROR_FAILURE;
00220 
00221   // Allocate the sniffer buffer...
00222   if (NS_SUCCEEDED(rv) && !mBuffer) {
00223     mBuffer = new char[MAX_BUFFER_SIZE];
00224 
00225     if (!mBuffer) {
00226       rv = NS_ERROR_OUT_OF_MEMORY;
00227     }
00228   }
00229 
00230   // Do not pass the OnStartRequest on to the next listener (yet)...
00231   return rv;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsUnknownDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
00236                                 nsresult aStatus)
00237 {
00238   nsresult rv = NS_OK;
00239 
00240   if (!mNextListener) return NS_ERROR_FAILURE;
00241 
00242   //
00243   // The total amount of data is less than the size of the sniffer buffer.
00244   // Analyze the buffer now...
00245   //
00246   if (mContentType.IsEmpty()) {
00247     DetermineContentType(request);
00248 
00249     NS_ASSERTION(!mContentType.IsEmpty(), 
00250                  "Content type should be known by now.");
00251     rv = FireListenerNotifications(request, aCtxt);
00252 
00253     if (NS_FAILED(rv)) {
00254       aStatus = rv;
00255     }
00256   }
00257 
00258   rv = mNextListener->OnStopRequest(request, aCtxt, aStatus);
00259   mNextListener = 0;
00260 
00261   return rv;
00262 }
00263 
00264 // ----
00265 //
00266 // nsIContentSniffer methods...
00267 //
00268 // ----
00269 NS_IMETHODIMP
00270 nsUnknownDecoder::GetMIMETypeFromContent(nsIRequest* aRequest,
00271                                          const PRUint8* aData,
00272                                          PRUint32 aLength,
00273                                          nsACString& type)
00274 {
00275   mBuffer = NS_CONST_CAST(char*, NS_REINTERPRET_CAST(const char*, aData));
00276   mBufferLen = aLength;
00277   DetermineContentType(aRequest);
00278   mBuffer = nsnull;
00279   mBufferLen = 0;
00280   type.Assign(mContentType);
00281   mContentType.Truncate();
00282   return NS_OK;
00283 }
00284 
00285 
00286 // Actual sniffing code
00287 
00288 PRBool nsUnknownDecoder::AllowSniffing(nsIRequest* aRequest)
00289 {
00290   if (!mRequireHTMLsuffix) {
00291     return PR_TRUE;
00292   }
00293   
00294   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00295   if (!channel) {
00296     NS_ERROR("QI failed");
00297     return PR_FALSE;
00298   }
00299 
00300   nsCOMPtr<nsIURI> uri;
00301   if (NS_FAILED(channel->GetURI(getter_AddRefs(uri))) || !uri) {
00302     return PR_FALSE;
00303   }
00304   
00305   PRBool isLocalFile = PR_FALSE;
00306   if (NS_FAILED(uri->SchemeIs("file", &isLocalFile)) || isLocalFile) {
00307     return PR_FALSE;
00308   }
00309 
00310   return PR_TRUE;
00311 }
00312 
00321 nsUnknownDecoder::nsSnifferEntry nsUnknownDecoder::sSnifferEntries[] = {
00322   SNIFFER_ENTRY("%PDF-", APPLICATION_PDF),
00323 
00324   SNIFFER_ENTRY("%!PS-Adobe-", APPLICATION_POSTSCRIPT),
00325   SNIFFER_ENTRY("%! PS-Adobe-", APPLICATION_POSTSCRIPT),
00326 
00327   // Files that start with mailbox delimiters let's provisionally call
00328   // text/plain
00329   SNIFFER_ENTRY("From", TEXT_PLAIN),
00330   SNIFFER_ENTRY(">From", TEXT_PLAIN),
00331 
00332   // If the buffer begins with "#!" or "%!" then it is a script of
00333   // some sort...  "Scripts" can include arbitrary data to be passed
00334   // to an interpreter, so we need to decide whether we can call this
00335   // text or whether it's data.
00336   SNIFFER_ENTRY_WITH_FUNC("#!", &nsUnknownDecoder::LastDitchSniff),
00337 
00338   // XXXbz should (and can) we also include the various ways that <?xml can
00339   // appear as UTF-16 and such?  See http://www.w3.org/TR/REC-xml#sec-guessing
00340   SNIFFER_ENTRY_WITH_FUNC("<?xml", &nsUnknownDecoder::SniffForXML)
00341 };
00342 
00343 PRUint32 nsUnknownDecoder::sSnifferEntryNum =
00344   sizeof(nsUnknownDecoder::sSnifferEntries) /
00345     sizeof(nsUnknownDecoder::nsSnifferEntry);
00346 
00347 void nsUnknownDecoder::DetermineContentType(nsIRequest* aRequest)
00348 {
00349   NS_ASSERTION(mContentType.IsEmpty(), "Content type is already known.");
00350   if (!mContentType.IsEmpty()) return;
00351 
00352   // First, run through all the types we can detect reliably based on
00353   // magic numbers
00354   PRUint32 i;
00355   for (i = 0; i < sSnifferEntryNum; ++i) {
00356     if (mBufferLen >= sSnifferEntries[i].mByteLen &&  // enough data
00357         memcmp(mBuffer, sSnifferEntries[i].mBytes, sSnifferEntries[i].mByteLen) == 0) {  // and type matches
00358       NS_ASSERTION(sSnifferEntries[i].mMimeType ||
00359                    sSnifferEntries[i].mContentTypeSniffer,
00360                    "Must have either a type string or a function to set the type");
00361       NS_ASSERTION(sSnifferEntries[i].mMimeType == nsnull ||
00362                    sSnifferEntries[i].mContentTypeSniffer == nsnull,
00363                    "Both at type string and a type sniffing function set;"
00364                    " using type string");
00365       if (sSnifferEntries[i].mMimeType) {
00366         mContentType = sSnifferEntries[i].mMimeType;
00367         return;
00368       }
00369       else if ((this->*(sSnifferEntries[i].mContentTypeSniffer))(aRequest)) {
00370         return;
00371       }        
00372     }
00373   }
00374 
00375   if (TryContentSniffers(aRequest)) {
00376     return;
00377   }
00378 
00379   if (SniffForHTML(aRequest)) {
00380     return;
00381   }
00382   
00383   // We don't know what this is yet.  Before we just give up, try
00384   // the URI from the request.
00385   if (SniffURI(aRequest)) {
00386     return;
00387   }
00388   
00389   LastDitchSniff(aRequest);
00390 }
00391 
00392 PRBool nsUnknownDecoder::TryContentSniffers(nsIRequest* aRequest)
00393 {
00394   // Enumerate content sniffers
00395   nsCOMPtr<nsICategoryManager> catMan(do_GetService("@mozilla.org/categorymanager;1"));
00396   if (!catMan) {
00397     return PR_FALSE;
00398   }
00399 
00400   nsCOMPtr<nsISimpleEnumerator> sniffers;
00401   catMan->EnumerateCategory("content-sniffing-services", getter_AddRefs(sniffers));
00402   if (!sniffers) {
00403     return PR_FALSE;
00404   }
00405 
00406   PRBool hasMore;
00407   while (NS_SUCCEEDED(sniffers->HasMoreElements(&hasMore)) && hasMore) {
00408     nsCOMPtr<nsISupports> elem;
00409     sniffers->GetNext(getter_AddRefs(elem));
00410     NS_ASSERTION(elem, "No element even though hasMore returned true!?");
00411 
00412     nsCOMPtr<nsISupportsCString> sniffer_id(do_QueryInterface(elem));
00413     NS_ASSERTION(sniffer_id, "element is no nsISupportsCString!?");
00414     nsCAutoString contractid;
00415     nsresult rv = sniffer_id->GetData(contractid);
00416     if (NS_FAILED(rv)) {
00417       continue;
00418     }
00419 
00420     nsCOMPtr<nsISupports> sniffer(do_GetService(contractid.get()));
00421     if (!sniffer) {
00422       continue;
00423     }
00424 
00425     nsCOMPtr<nsIContentSniffer> sniffer1(do_QueryInterface(sniffer));
00426     nsCOMPtr<nsIContentSniffer_MOZILLA_1_8_BRANCH> sniffer2 =
00427       do_QueryInterface(sniffer);
00428     if (sniffer2) {
00429       rv = sniffer2->GetMIMETypeFromContent(aRequest, (const PRUint8*)mBuffer,
00430                                             mBufferLen, mContentType);
00431     } else if (sniffer1) {
00432       rv = sniffer1->GetMIMETypeFromContent((const PRUint8*)mBuffer,
00433                                             mBufferLen, mContentType);
00434     } else {
00435       continue;
00436     }
00437 
00438     if (NS_SUCCEEDED(rv)) {
00439       return PR_TRUE;
00440     }
00441   }
00442 
00443   return PR_FALSE;
00444 }
00445 
00446 PRBool nsUnknownDecoder::SniffForHTML(nsIRequest* aRequest)
00447 {
00448   /*
00449    * To prevent a possible attack, we will not consider this to be
00450    * html content if it comes from the local file system and our prefs
00451    * are set right
00452    */
00453   if (!AllowSniffing(aRequest)) {
00454     return PR_FALSE;
00455   }
00456   
00457   // Now look for HTML.
00458   const char* str = mBuffer;
00459   const char* end = mBuffer + mBufferLen;
00460 
00461   // skip leading whitespace
00462   while (str != end && nsCRT::IsAsciiSpace(*str)) {
00463     ++str;
00464   }
00465 
00466   // did we find something like a start tag?
00467   if (str == end || *str != '<' || ++str == end) {
00468     return PR_FALSE;
00469   }
00470 
00471   // If we seem to be SGML or XML and we got down here, just pretend we're HTML
00472   if (*str == '!' || *str == '?') {
00473     mContentType = TEXT_HTML;
00474     return PR_TRUE;
00475   }
00476   
00477   PRUint32 bufSize = end - str;
00478   // We use sizeof(_tagstr) below because that's the length of _tagstr
00479   // with the one char " " or ">" appended.
00480 #define MATCHES_TAG(_tagstr)                                              \
00481   (bufSize >= sizeof(_tagstr) &&                                          \
00482    (PL_strncasecmp(str, _tagstr " ", sizeof(_tagstr)) == 0 ||             \
00483     PL_strncasecmp(str, _tagstr ">", sizeof(_tagstr)) == 0))
00484     
00485   if (MATCHES_TAG("html")     ||
00486       MATCHES_TAG("frameset") ||
00487       MATCHES_TAG("body")     ||
00488       MATCHES_TAG("head")     ||
00489       MATCHES_TAG("script")   ||
00490       MATCHES_TAG("iframe")   ||
00491       MATCHES_TAG("a")        ||
00492       MATCHES_TAG("img")      ||
00493       MATCHES_TAG("table")    ||
00494       MATCHES_TAG("title")    ||
00495       MATCHES_TAG("link")     ||
00496       MATCHES_TAG("base")     ||
00497       MATCHES_TAG("style")    ||
00498       MATCHES_TAG("div")      ||
00499       MATCHES_TAG("p")        ||
00500       MATCHES_TAG("font")     ||
00501       MATCHES_TAG("applet")   ||
00502       MATCHES_TAG("meta")     ||
00503       MATCHES_TAG("center")   ||
00504       MATCHES_TAG("form")     ||
00505       MATCHES_TAG("isindex")  ||
00506       MATCHES_TAG("h1")       ||
00507       MATCHES_TAG("h2")       ||
00508       MATCHES_TAG("h3")       ||
00509       MATCHES_TAG("h4")       ||
00510       MATCHES_TAG("h5")       ||
00511       MATCHES_TAG("h6")       ||
00512       MATCHES_TAG("b")        ||
00513       MATCHES_TAG("pre")) {
00514   
00515     mContentType = TEXT_HTML;
00516     return PR_TRUE;
00517   }
00518 
00519 #undef MATCHES_TAG
00520   
00521   return PR_FALSE;
00522 }
00523 
00524 PRBool nsUnknownDecoder::SniffForXML(nsIRequest* aRequest)
00525 {
00526   // Just like HTML, this should be able to be shut off.
00527   if (!AllowSniffing(aRequest)) {
00528     return PR_FALSE;
00529   }
00530 
00531   // First see whether we can glean anything from the uri...
00532   if (!SniffURI(aRequest)) {
00533     // Oh well; just generic XML will have to do
00534     mContentType = TEXT_XML;
00535   }
00536   
00537   return PR_TRUE;
00538 }
00539 
00540 PRBool nsUnknownDecoder::SniffURI(nsIRequest* aRequest)
00541 {
00542   nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1"));
00543   if (mimeService) {
00544     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00545     if (channel) {
00546       nsCOMPtr<nsIURI> uri;
00547       nsresult result = channel->GetURI(getter_AddRefs(uri));
00548       if (NS_SUCCEEDED(result) && uri) {
00549         nsCAutoString type;
00550         result = mimeService->GetTypeFromURI(uri, type);
00551         if (NS_SUCCEEDED(result)) {
00552           mContentType = type;
00553           return PR_TRUE;
00554         }
00555       }
00556     }
00557   }
00558 
00559   return PR_FALSE;
00560 }
00561 
00562 // This macro is based on RFC 2046 Section 4.1.2.  Treat any char 0-31
00563 // except the 9-13 range (\t, \n, \v, \f, \r) and char 27 (used by
00564 // encodings like Shift_JIS) as non-text
00565 #define IS_TEXT_CHAR(ch)                                     \
00566   (((unsigned char)(ch)) > 31 || (9 <= (ch) && (ch) <= 13) || (ch) == 27)
00567 
00568 PRBool nsUnknownDecoder::LastDitchSniff(nsIRequest* aRequest)
00569 {
00570   // All we can do now is try to guess whether this is text/plain or
00571   // application/octet-stream
00572 
00573   // First, check for a BOM.  If we see one, assume this is text/plain
00574   // in whatever encoding.  If there is a BOM _and_ text we will
00575   // always have at least 4 bytes in the buffer (since the 2-byte BOMs
00576   // are for 2-byte encodings and the UTF-8 BOM is 3 bytes).
00577   if (mBufferLen >= 4) {
00578     const unsigned char* buf = (const unsigned char*)mBuffer;
00579     if ((buf[0] == 0xFE && buf[1] == 0xFF) || // UTF-16BE
00580         (buf[0] == 0xFF && buf[1] == 0xFE) || // UTF-16LE
00581         (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) || // UTF-8
00582         (buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF) || // UCS-4BE
00583         (buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFF && buf[3] == 0xFE)) { // UCS-4
00584         
00585       mContentType = TEXT_PLAIN;
00586       return PR_TRUE;
00587     }
00588   }
00589   
00590   // Now see whether the buffer has any non-text chars.  If not, then let's
00591   // just call it text/plain...
00592   //
00593   PRUint32 i;
00594   for (i=0; i<mBufferLen && IS_TEXT_CHAR(mBuffer[i]); i++);
00595 
00596   if (i == mBufferLen) {
00597     mContentType = TEXT_PLAIN;
00598   }
00599   else {
00600     mContentType = APPLICATION_OCTET_STREAM;
00601   }
00602 
00603   return PR_TRUE;    
00604 }
00605 
00606 
00607 nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
00608                                                      nsISupports *aCtxt)
00609 {
00610   nsresult rv = NS_OK;
00611 
00612   if (!mNextListener) return NS_ERROR_FAILURE;
00613 
00614   nsCOMPtr<nsIViewSourceChannel> viewSourceChannel = do_QueryInterface(request);
00615   if (viewSourceChannel) {
00616     rv = viewSourceChannel->SetOriginalContentType(mContentType);
00617   } else {
00618     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
00619     if (NS_SUCCEEDED(rv)) {
00620       // Set the new content type on the channel...
00621       rv = channel->SetContentType(mContentType);
00622     }
00623   }
00624 
00625   NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to set content type on channel!");
00626   if (NS_FAILED(rv)) {
00627     // Cancel the request to make sure it has the correct status if
00628     // mNextListener looks at it.
00629     request->Cancel(rv);
00630     mNextListener->OnStartRequest(request, aCtxt);
00631     return rv;
00632   }
00633 
00634   // Fire the OnStartRequest(...)
00635   rv = mNextListener->OnStartRequest(request, aCtxt);
00636 
00637   if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY;
00638 
00639   // If the request was canceled, then we need to treat that equivalently
00640   // to an error returned by OnStartRequest.
00641   if (NS_SUCCEEDED(rv))
00642     request->GetStatus(&rv);
00643 
00644   // Fire the first OnDataAvailable for the data that was read from the
00645   // stream into the sniffer buffer...
00646   if (NS_SUCCEEDED(rv) && (mBufferLen > 0)) {
00647     PRUint32 len = 0;
00648     nsCOMPtr<nsIInputStream> in;
00649     nsCOMPtr<nsIOutputStream> out;
00650 
00651     // Create a pipe and fill it with the data from the sniffer buffer.
00652     rv = NS_NewPipe(getter_AddRefs(in), getter_AddRefs(out),
00653                     MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
00654 
00655     if (NS_SUCCEEDED(rv)) {
00656       rv = out->Write(mBuffer, mBufferLen, &len);
00657       if (NS_SUCCEEDED(rv)) {
00658         if (len == mBufferLen) {
00659           rv = mNextListener->OnDataAvailable(request, aCtxt, in, 0, len);
00660         } else {
00661           NS_ERROR("Unable to write all the data into the pipe.");
00662           rv = NS_ERROR_FAILURE;
00663         }
00664       }
00665     }
00666   }
00667 
00668   delete [] mBuffer;
00669   mBuffer = nsnull;
00670   mBufferLen = 0;
00671 
00672   return rv;
00673 }
00674 
00675 void
00676 nsBinaryDetector::DetermineContentType(nsIRequest* aRequest)
00677 {
00678   LastDitchSniff(aRequest);
00679   if (mContentType.Equals(APPLICATION_OCTET_STREAM)) {
00680     // We want to guess at it instead
00681     mContentType = APPLICATION_GUESS_FROM_EXT;
00682   }
00683 }