netwerk/streamconv/converters/nsBinHexDecoder.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsIOService.h"
     7 #include "nsBinHexDecoder.h"
     8 #include "nsIServiceManager.h"
     9 #include "nsIStreamConverterService.h"
    10 #include "nsCRT.h"
    11 #include "nsIPipe.h"
    12 #include "nsMimeTypes.h"
    13 #include "netCore.h"
    14 #include "nsXPIDLString.h"
    15 #include "prnetdb.h"
    16 #include "nsIURI.h"
    17 #include "nsIURL.h"
    19 #include "nsIMIMEService.h"
    20 #include "nsMimeTypes.h"
    21 #include <algorithm>
    23 nsBinHexDecoder::nsBinHexDecoder() :
    24   mState(0), mCRC(0), mFileCRC(0), mOctetin(26),
    25   mDonePos(3), mInCRC(0), mCount(0), mMarker(0), mPosInbuff(0),
    26   mPosOutputBuff(0)
    27 {
    28   mDataBuffer = nullptr;
    29   mOutgoingBuffer = nullptr;
    31   mOctetBuf.val = 0;
    32   mHeader.type = 0;
    33   mHeader.creator = 0;
    34   mHeader.flags = 0;
    35   mHeader.dlen = 0;
    36   mHeader.rlen = 0;
    37 }
    39 nsBinHexDecoder::~nsBinHexDecoder()
    40 {
    41   if (mDataBuffer)
    42     nsMemory::Free(mDataBuffer);
    43   if (mOutgoingBuffer)
    44     nsMemory::Free(mOutgoingBuffer);
    45 }
    47 NS_IMPL_ADDREF(nsBinHexDecoder)
    48 NS_IMPL_RELEASE(nsBinHexDecoder)
    50 NS_INTERFACE_MAP_BEGIN(nsBinHexDecoder)
    51   NS_INTERFACE_MAP_ENTRY(nsIStreamConverter)
    52   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    53   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    54   NS_INTERFACE_MAP_ENTRY(nsISupports)
    55 NS_INTERFACE_MAP_END
    58 // The binhex 4.0 decoder table....
    60 static const signed char binhex_decode[256] =
    61 {
    62   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    63   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    64   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
    65   13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1,
    66   22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1,
    67   37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1,
    68   48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1,
    69   61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    70   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    71   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    72   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    73   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    74   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    75   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    76   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    77   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    78 };
    80 #define BHEXVAL(c) (binhex_decode[(unsigned char) c])
    82 //////////////////////////////////////////////////////
    83 // nsIStreamConverter methods...
    84 //////////////////////////////////////////////////////
    86 NS_IMETHODIMP
    87 nsBinHexDecoder::Convert(nsIInputStream *aFromStream,
    88                          const char *aFromType,
    89                          const char *aToType,
    90                          nsISupports *aCtxt,
    91                          nsIInputStream **aResultStream)
    92 {
    93   return NS_ERROR_NOT_IMPLEMENTED;
    94 }
    96 NS_IMETHODIMP
    97 nsBinHexDecoder::AsyncConvertData(const char *aFromType,
    98                                   const char *aToType,
    99                                   nsIStreamListener *aListener,
   100                                   nsISupports *aCtxt)
   101 {
   102   NS_ASSERTION(aListener && aFromType && aToType,
   103                "null pointer passed into bin hex converter");
   105   // hook up our final listener. this guy gets the various On*() calls we want to throw
   106   // at him.
   107   //
   108   mNextListener = aListener;
   109   return (aListener) ? NS_OK : NS_ERROR_FAILURE;
   110 }
   112 //////////////////////////////////////////////////////
   113 // nsIStreamListener methods...
   114 //////////////////////////////////////////////////////
   115 NS_IMETHODIMP
   116 nsBinHexDecoder::OnDataAvailable(nsIRequest* request,
   117                                  nsISupports *aCtxt,
   118                                  nsIInputStream *aStream,
   119                                  uint64_t aSourceOffset,
   120                                  uint32_t aCount)
   121 {
   122   nsresult rv = NS_OK;
   124   if (mOutputStream && mDataBuffer && aCount > 0)
   125   {
   126     uint32_t numBytesRead = 0;
   127     while (aCount > 0) // while we still have bytes to copy...
   128     {
   129       aStream->Read(mDataBuffer, std::min(aCount, nsIOService::gDefaultSegmentSize - 1), &numBytesRead);
   130       if (aCount >= numBytesRead)
   131         aCount -= numBytesRead; // subtract off the number of bytes we just read
   132       else
   133         aCount = 0;
   135       // Process this new chunk of bin hex data...
   136       ProcessNextChunk(request, aCtxt, numBytesRead);
   137     }
   138   }
   140   return rv;
   141 }
   143 nsresult nsBinHexDecoder::ProcessNextState(nsIRequest * aRequest, nsISupports * aContext)
   144 {
   145   nsresult status = NS_OK;
   146   uint16_t tmpcrc, cval;
   147   unsigned char ctmp, c = mRlebuf;
   149   /* do CRC */
   150   ctmp = mInCRC ? c : 0;
   151   cval = mCRC & 0xf000;
   152   tmpcrc = ((uint16_t) (mCRC << 4) | (ctmp >> 4)) ^ (cval | (cval >> 7) | (cval >> 12));
   153   cval = tmpcrc & 0xf000;
   154   mCRC = ((uint16_t) (tmpcrc << 4) | (ctmp & 0x0f)) ^ (cval | (cval >> 7) | (cval >> 12));
   156   /* handle state */
   157   switch (mState)
   158   {
   159     case BINHEX_STATE_START:
   160       mState = BINHEX_STATE_FNAME;
   161       mCount = 0;
   163       // c & 63 returns the length of mName. So if we need the length, that's how
   164       // you can figure it out....
   165       mName.SetLength(c & 63);
   166       if (mName.Length() != (c & 63)) {
   167         /* XXX ProcessNextState/ProcessNextChunk aren't rv checked */
   168         mState = BINHEX_STATE_DONE;
   169       }
   170       break;
   172     case BINHEX_STATE_FNAME:
   173       mName.BeginWriting()[mCount] = c;
   175       if (++mCount > mName.Length())
   176       {
   177         // okay we've figured out the file name....set the content type on the channel
   178         // based on the file name AND issue our delayed on start request....
   180         DetectContentType(aRequest, mName);
   181         // now propagate the on start request
   182         mNextListener->OnStartRequest(aRequest, aContext);
   184         mState = BINHEX_STATE_HEADER;
   185         mCount = 0;
   186       }
   187       break;
   189     case BINHEX_STATE_HEADER:
   190       ((char *) &mHeader)[mCount] = c;
   191       if (++mCount == 18)
   192       {
   193         if (sizeof(binhex_header) != 18)  /* fix an alignment problem in some OSes */
   194         {
   195           char *p = (char *)&mHeader;
   196           p += 19;
   197           for (c = 0; c < 8; c++)
   198           {
   199             *p = *(p-2);
   200             --p;
   201           }
   202         }
   204         mState = BINHEX_STATE_HCRC;
   205         mInCRC = 1;
   206         mCount = 0;
   207       }
   208       break;
   210     case BINHEX_STATE_DFORK:
   211     case BINHEX_STATE_RFORK:
   212       mOutgoingBuffer[mPosOutputBuff++] = c;
   213       if (--mCount == 0)
   214       {
   215         /* only output data fork in the non-mac system.      */
   216         if (mState == BINHEX_STATE_DFORK)
   217         {
   218           uint32_t numBytesWritten = 0;
   219           mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten);
   220           if (int32_t(numBytesWritten) != mPosOutputBuff)
   221             status = NS_ERROR_FAILURE;
   223           // now propagate the data we just wrote
   224           mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten);
   225         }
   226         else
   227           status = NS_OK;        /* do nothing for resource fork.  */
   229         mPosOutputBuff = 0;
   231         if (status != NS_OK)
   232           mState = BINHEX_STATE_DONE;
   233         else
   234           ++mState;
   236         mInCRC = 1;
   237       }
   238       else if (mPosOutputBuff >= (int32_t) nsIOService::gDefaultSegmentSize)
   239       {
   240         if (mState == BINHEX_STATE_DFORK)
   241         {
   242           uint32_t numBytesWritten = 0;
   243           mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten);
   244           if (int32_t(numBytesWritten) != mPosOutputBuff)
   245             status = NS_ERROR_FAILURE;
   247           mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten);
   248           mPosOutputBuff = 0;
   249         }
   250       }
   251       break;
   253     case BINHEX_STATE_HCRC:
   254     case BINHEX_STATE_DCRC:
   255     case BINHEX_STATE_RCRC:
   256       if (!mCount++)
   257         mFileCRC = (unsigned short) c << 8;
   258       else
   259       {
   260         if ((mFileCRC | c) != mCRC)
   261         {
   262           mState = BINHEX_STATE_DONE;
   263           break;
   264         }
   266         /* passed the CRC check!!!*/
   267         mCRC = 0;
   268         if (++mState == BINHEX_STATE_FINISH)
   269         {
   270           // when we reach the finished state...fire an on stop request on the event listener...
   271           mNextListener->OnStopRequest(aRequest, aContext, NS_OK);
   272           mNextListener = 0;
   274           /*   now We are done with everything.  */
   275           ++mState;
   276           break;
   277         }
   279         if (mState == BINHEX_STATE_DFORK)
   280           mCount = PR_ntohl(mHeader.dlen);
   281         else
   282         {
   283           // we aren't processing the resurce Fork. uncomment this line if we make this converter
   284           // smart enough to do this in the future.
   285           // mCount = PR_ntohl(mHeader.rlen);  /* it should in host byte order */
   286           mCount = 0;
   287         }
   289         if (mCount) {
   290           mInCRC = 0;
   291         } else {
   292           /* nothing inside, so skip to the next state. */
   293           ++mState;
   294         }
   295       }
   296       break;
   297   }
   299   return NS_OK;
   300 }
   302 nsresult nsBinHexDecoder::ProcessNextChunk(nsIRequest * aRequest, nsISupports * aContext, uint32_t numBytesInBuffer)
   303 {
   304   bool foundStart;
   305   int16_t octetpos, c = 0;
   306   uint32_t val;
   307   mPosInDataBuffer = 0; // use member variable.
   309   NS_ENSURE_TRUE(numBytesInBuffer > 0, NS_ERROR_FAILURE);
   311   //  if it is the first time, seek to the right start place.
   312   if (mState == BINHEX_STATE_START)
   313   {
   314     foundStart = false;
   315     // go through the line, until we get a ':'
   316     while (mPosInDataBuffer < numBytesInBuffer)
   317     {
   318       c = mDataBuffer[mPosInDataBuffer++];
   319       while (c == nsCRT::CR || c == nsCRT::LF)
   320       {
   321         if (mPosInDataBuffer >= numBytesInBuffer)
   322           break;
   324         c = mDataBuffer[mPosInDataBuffer++];
   325         if (c == ':')
   326         {
   327           foundStart = true;
   328           break;
   329         }
   330       }
   331       if (foundStart)  break;    /* we got the start point. */
   332     }
   334     if (mPosInDataBuffer >= numBytesInBuffer)
   335       return NS_OK;      /* we meet buff end before we get the start point, wait till next fills. */
   337     if (c != ':')
   338       return NS_ERROR_FAILURE;    /* can't find the start character.  */
   339   }
   341   while (mState != BINHEX_STATE_DONE)
   342   {
   343     /* fill in octetbuf */
   344     do
   345     {
   346       if (mPosInDataBuffer >= numBytesInBuffer)
   347         return NS_OK;      /* end of buff, go on for the nxet calls. */
   349       c = GetNextChar(numBytesInBuffer);
   350       if (c == 0)  return NS_OK;
   352       if ((val = BHEXVAL(c)) == uint32_t(-1))
   353       {
   354         /* we incount an invalid character.  */
   355         if (c)
   356         {
   357           /* rolling back. */
   358           --mDonePos;
   359           if (mOctetin >= 14)
   360             --mDonePos;
   361           if (mOctetin >= 20)
   362             --mDonePos;
   363         }
   364         break;
   365       }
   366       mOctetBuf.val |= val << mOctetin;
   367     }
   368     while ((mOctetin -= 6) > 2);
   370     /* handle decoded characters -- run length encoding (rle) detection */
   372     // We put decoded chars into mOctetBuf.val in order from high to low (via
   373     // bitshifting, above).  But we want to byte-address them, so we want the
   374     // first byte to correspond to the high byte.  In other words, we want
   375     // these bytes to be in network order.
   376     mOctetBuf.val = PR_htonl(mOctetBuf.val);
   378     for (octetpos = 0; octetpos < mDonePos; ++octetpos)
   379     {
   380       c = mOctetBuf.c[octetpos];
   382       if (c == 0x90 && !mMarker++)
   383         continue;
   385       if (mMarker)
   386       {
   387         if (c == 0)
   388         {
   389           mRlebuf = 0x90;
   390           ProcessNextState(aRequest, aContext);
   391         }
   392         else
   393         {
   394           /* we are in the run length mode */
   395           while (--c > 0)
   396             ProcessNextState(aRequest, aContext);
   397         }
   398         mMarker = 0;
   399       }
   400       else
   401       {
   402         mRlebuf = (unsigned char) c;
   403         ProcessNextState(aRequest, aContext);
   404       }
   406       if (mState >= BINHEX_STATE_DONE)
   407         break;
   408     }
   410     /* prepare for next 3 characters.  */
   411     if (mDonePos < 3 && mState < BINHEX_STATE_DONE)
   412       mState = BINHEX_STATE_DONE;
   414     mOctetin = 26;
   415     mOctetBuf.val = 0;
   416   }
   418   return   NS_OK;
   419 }
   421 int16_t nsBinHexDecoder::GetNextChar(uint32_t numBytesInBuffer)
   422 {
   423   char c = 0;
   425   while (mPosInDataBuffer < numBytesInBuffer)
   426   {
   427     c = mDataBuffer[mPosInDataBuffer++];
   428     if (c != nsCRT::LF && c != nsCRT::CR)
   429       break;
   430   }
   431   return (c == nsCRT::LF || c == nsCRT::CR) ? 0 : (int) c;
   432 }
   434 //////////////////////////////////////////////////////
   435 // nsIRequestObserver methods...
   436 //////////////////////////////////////////////////////
   438 NS_IMETHODIMP
   439 nsBinHexDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt)
   440 {
   441   nsresult rv = NS_OK;
   443   NS_ENSURE_TRUE(mNextListener, NS_ERROR_FAILURE);
   445   mDataBuffer = (char *) moz_malloc((sizeof(char) * nsIOService::gDefaultSegmentSize));
   446   mOutgoingBuffer = (char *) moz_malloc((sizeof(char) * nsIOService::gDefaultSegmentSize));
   447   if (!mDataBuffer || !mOutgoingBuffer) return NS_ERROR_FAILURE; // out of memory;
   449   // now we want to create a pipe which we'll use to write our converted data...
   450   rv = NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream),
   451                   nsIOService::gDefaultSegmentSize,
   452                   nsIOService::gDefaultSegmentSize,
   453                   true, true);
   455   // don't propagate the on start request to mNextListener until we have determined the content type.
   456   return rv;
   457 }
   459 // Given the fileName we discovered inside the bin hex decoding, figure out the
   460 // content type and set it on the channel associated with the request.  If the
   461 // filename tells us nothing useful, just report an unknown type and let the
   462 // unknown decoder handle things.
   463 nsresult nsBinHexDecoder::DetectContentType(nsIRequest* aRequest,
   464                                             const nsAFlatCString &aFilename)
   465 {
   466   if (aFilename.IsEmpty()) {
   467     // Nothing to do here.
   468     return NS_OK;
   469   }
   471   nsresult rv;
   472   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest, &rv));
   473   NS_ENSURE_SUCCESS(rv, rv);
   475   nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
   476   NS_ENSURE_SUCCESS(rv, rv);
   478   nsAutoCString contentType;
   480   // extract the extension from aFilename and look it up.
   481   const char * fileExt = strrchr(aFilename.get(), '.');
   482   if (!fileExt) {
   483     return NS_OK;
   484   }
   486   mimeService->GetTypeFromExtension(nsDependentCString(fileExt), contentType);
   488   // Only set the type if it's not empty and, to prevent recursive loops, not the binhex type
   489   if (!contentType.IsEmpty() && !contentType.Equals(APPLICATION_BINHEX)) {
   490     channel->SetContentType(contentType);
   491   } else {
   492     channel->SetContentType(NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
   493   }
   495   return NS_OK;
   496 }
   499 NS_IMETHODIMP
   500 nsBinHexDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
   501                                 nsresult aStatus)
   502 {
   503   nsresult rv = NS_OK;
   505   if (!mNextListener) return NS_ERROR_FAILURE;
   506   // don't do anything here...we'll fire our own on stop request when we are done
   507   // processing the data....
   509   return rv;
   510 }

mercurial