Back to index

lightning-sunbird  0.9+nobinonly
nsBinHexDecoder.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  *   Scott MacGregor <mscott@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsBinHexDecoder.h"
00040 #include "nsIServiceManager.h"
00041 #include "nsIStreamConverterService.h"
00042 #include "nsCRT.h"
00043 #include "nsIPipe.h"
00044 #include "nsMimeTypes.h"
00045 #include "netCore.h"
00046 #include "nsXPIDLString.h"
00047 #include "prnetdb.h"
00048 #include "nsIURI.h"
00049 #include "nsIURL.h"
00050 
00051 #include "nsIMIMEService.h"
00052 #include "nsMimeTypes.h"
00053 
00054 
00055 #define DATA_BUFFER_SIZE (4096*2) 
00056 
00057 #define NS_STREAM_CONVERTER_SEGMENT_SIZE   (4*1024)
00058 #define NS_STREAM_CONVERTER_BUFFER_SIZE    (32*1024)
00059 
00060 // sadly I couldn't find char defintions for CR LF elsehwere in the code (they are defined as strings in nsCRT.h)
00061 #define CR  '\015'
00062 #define LF '\012'
00063 
00064 nsBinHexDecoder::nsBinHexDecoder() :
00065   mState(0), mCRC(0), mFileCRC(0), mOctetin(26), 
00066   mDonePos(3), mInCRC(0), mCount(0), mMarker(0), mPosInbuff(0), 
00067   mPosOutputBuff(0)
00068 {
00069   mDataBuffer = nsnull;
00070   mOutgoingBuffer = nsnull;
00071 
00072   mOctetBuf.val      = 0;
00073   mHeader.type = 0;
00074   mHeader.creator = 0;
00075   mHeader.flags = 0;
00076   mHeader.dlen = 0;
00077   mHeader.rlen = 0;
00078 }
00079 
00080 nsBinHexDecoder::~nsBinHexDecoder()
00081 {
00082   if (mDataBuffer)
00083     nsMemory::Free(mDataBuffer);
00084   if (mOutgoingBuffer)
00085     nsMemory::Free(mOutgoingBuffer);
00086 }
00087 
00088 NS_IMPL_ADDREF(nsBinHexDecoder)
00089 NS_IMPL_RELEASE(nsBinHexDecoder)
00090 
00091 NS_INTERFACE_MAP_BEGIN(nsBinHexDecoder)
00092    NS_INTERFACE_MAP_ENTRY(nsIStreamConverter)
00093    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00094    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00095    NS_INTERFACE_MAP_ENTRY(nsISupports)
00096 NS_INTERFACE_MAP_END
00097 
00098 
00099 // The binhex 4.0 decoder table....
00100 
00101 static char binhex_decode[256] = 
00102 {
00103        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00104        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00105        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
00106        13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1,
00107        22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1,
00108        37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1,
00109        48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1,
00110        61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00111        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00112        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00113        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00114        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00115        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00116        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00117        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00118        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
00119 };
00120 
00121 #define BHEXVAL(c) (binhex_decode[(unsigned char) c])
00122 
00124 // nsIStreamConverter methods...
00126 
00127 NS_IMETHODIMP
00128 nsBinHexDecoder::Convert(nsIInputStream *aFromStream,
00129                          const char *aFromType,
00130                          const char *aToType,
00131                          nsISupports *aCtxt, 
00132                          nsIInputStream **aResultStream) 
00133 {
00134   return NS_ERROR_NOT_IMPLEMENTED;
00135 }
00136 
00137 NS_IMETHODIMP
00138 nsBinHexDecoder::AsyncConvertData(const char *aFromType, 
00139                                   const char *aToType,
00140                                   nsIStreamListener *aListener, 
00141                                   nsISupports *aCtxt)
00142 {
00143   NS_ASSERTION(aListener && aFromType && aToType, 
00144                "null pointer passed into bin hex converter");
00145 
00146   // hook up our final listener. this guy gets the various On*() calls we want to throw
00147   // at him.
00148   //
00149   mNextListener = aListener;
00150   return (aListener) ? NS_OK : NS_ERROR_FAILURE;
00151 }
00152 
00154 // nsIStreamListener methods...
00156 NS_IMETHODIMP
00157 nsBinHexDecoder::OnDataAvailable(nsIRequest* request, 
00158                                   nsISupports *aCtxt,
00159                                   nsIInputStream *aStream, 
00160                                   PRUint32 aSourceOffset, 
00161                                   PRUint32 aCount)
00162 {
00163   nsresult rv = NS_OK;
00164 
00165   if (mOutputStream && mDataBuffer && aCount > 0)
00166   {
00167     PRUint32 numBytesRead = 0; 
00168     while (aCount > 0) // while we still have bytes to copy...
00169     {
00170       aStream->Read(mDataBuffer, PR_MIN(aCount, DATA_BUFFER_SIZE - 1), &numBytesRead);
00171       if (aCount >= numBytesRead)
00172         aCount -= numBytesRead; // subtract off the number of bytes we just read
00173       else
00174         aCount = 0;
00175 
00176       // Process this new chunk of bin hex data...
00177       ProcessNextChunk(request, aCtxt, numBytesRead);
00178     }
00179   }
00180 
00181   return rv;
00182 }
00183 
00184 nsresult nsBinHexDecoder::ProcessNextState(nsIRequest * aRequest, nsISupports * aContext)
00185 {
00186        nsresult status = NS_OK;
00187        PRUint16      tmpcrc, cval;
00188        unsigned char  ctmp, c = mRlebuf;
00189        
00190        /* do CRC */
00191        ctmp = mInCRC ? c : 0;
00192        cval = mCRC & 0xf000;
00193        tmpcrc = ((PRUint16) (mCRC << 4) | (ctmp >> 4)) ^ (cval | (cval >> 7) | (cval >> 12));
00194        cval = tmpcrc & 0xf000;
00195        mCRC = ((PRUint16) (tmpcrc << 4) | (ctmp & 0x0f)) ^ (cval | (cval >> 7) | (cval >> 12));
00196 
00197        /* handle state */
00198        switch (mState) 
00199        {
00200               case BINHEX_STATE_START:
00201                      mState = BINHEX_STATE_FNAME;
00202                      mCount = 1;
00203 
00204       // c & 63 returns the length of mName. So if we need the length, that's how
00205       // you can figure it out...for now we are storing it in the first byte of mName
00206                      *(mName) = (c & 63);     
00207                      break;
00208                      
00209               case BINHEX_STATE_FNAME:
00210                      mName[mCount] = c;
00211                      
00212                      if (mCount++ > *(mName)) // the first byte of mName is the length...
00213                      {                    
00214         // okay we've figured out the file name....set the content type on the channel
00215         // based on the file name AND issue our delayed on start request....
00216         // be sure to skip the first byte of mName which is the size of the file name
00217         
00218         SetContentType(aRequest, &mName[1]);
00219         // now propagate the on start request
00220         mNextListener->OnStartRequest(aRequest, aContext);
00221 
00222                             mState = BINHEX_STATE_HEADER;
00223                             mCount = 0;
00224                      }
00225                      break;
00226                      
00227               case BINHEX_STATE_HEADER:
00228                      ((char *) &mHeader)[mCount] = c;
00229                      if (++mCount == 18) 
00230                      {
00231                             if (sizeof(binhex_header) != 18)   /* fix an alignment problem in some OSes */
00232                             {
00233                                    char *p = (char *)&mHeader;
00234                                    p += 19;
00235                                    for (c = 0; c < 8; c++)
00236           {
00237                                           *p = *(p-2);  p--;
00238           }
00239                             }
00240 
00241         mState = BINHEX_STATE_HCRC;
00242                             mInCRC = 1;
00243                             mCount = 0;
00244                      }
00245                      break;
00246                      
00247               case BINHEX_STATE_DFORK:
00248               case BINHEX_STATE_RFORK:
00249                      mOutgoingBuffer[mPosOutputBuff++] = c;
00250                      if (-- mCount == 0) 
00251                      {
00252                      /* only output data fork in the non-mac system.                */
00253                             if (mState == BINHEX_STATE_DFORK)
00254                             {
00255           PRUint32 numBytesWritten = 0; 
00256           mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten);
00257           if (PRInt32(numBytesWritten) != mPosOutputBuff)
00258             status = NS_ERROR_FAILURE;
00259 
00260           // now propagate the data we just wrote
00261           mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten);
00262                             }
00263                             else
00264                                    status = NS_OK;                           /* do nothing for resource fork.   */
00265 
00266                             mPosOutputBuff = 0;
00267                             
00268                             if (status != NS_OK)
00269                                    mState = BINHEX_STATE_DONE;
00270                             else
00271                                    mState ++;
00272                             
00273         mInCRC = 1;
00274                      }
00275                      else if (mPosOutputBuff >= DATA_BUFFER_SIZE)
00276                      {                           
00277                             if (mState == BINHEX_STATE_DFORK)
00278                             {
00279           PRUint32 numBytesWritten = 0; 
00280           mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten);
00281           if (PRInt32(numBytesWritten) != mPosOutputBuff)
00282             status = NS_ERROR_FAILURE;
00283 
00284           mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten);
00285           mPosOutputBuff = 0;
00286         }                                               
00287                      }
00288                      break;
00289                      
00290               case BINHEX_STATE_HCRC:
00291               case BINHEX_STATE_DCRC:
00292               case BINHEX_STATE_RCRC:
00293                      if (!mCount++) 
00294                             mFileCRC = (unsigned short) c << 8;
00295                      else 
00296                      {
00297                             if ((mFileCRC | c) != mCRC) 
00298                             {
00299                                    mState = BINHEX_STATE_DONE;
00300                                    break;
00301                             }
00302                             
00303                             /* passed the CRC check!!!*/
00304                             mCRC = 0;
00305                             if (++ mState == BINHEX_STATE_FINISH) 
00306                             { 
00307           // when we reach the finished state...fire an on stop request on the event listener...
00308           mNextListener->OnStopRequest(aRequest, aContext, NS_OK);
00309           mNextListener = 0;
00310       
00311           /*  now We are done with everything.   */            
00312                                    mState++;
00313                                    break;
00314                             }
00315                             
00316                             if (mState == BINHEX_STATE_DFORK) 
00317                                    mCount = PR_ntohl(mHeader.dlen);
00318                             else
00319                             {
00320           // we aren't processing the resurce Fork. uncomment this line if we make this converter
00321           // smart enough to do this in the future.
00322                                    // mCount = PR_ntohl(mHeader.rlen);       /* it should in host byte order */
00323           mCount = 0; 
00324                             }
00325                             
00326                             if (mCount) 
00327                                    mInCRC = 0;                                      
00328                             else 
00329                                    /* nothing inside, so skip to the next state. */
00330                                    mState ++;
00331                      }
00332                      break;
00333        }
00334        
00335        return NS_OK;
00336 }
00337 
00338 nsresult nsBinHexDecoder::ProcessNextChunk(nsIRequest * aRequest, nsISupports * aContext, PRUint32 numBytesInBuffer)
00339 {
00340        PRBool foundStart;
00341        PRInt16 octetpos, c = 0;
00342        PRUint32             val;
00343   mPosInDataBuffer  = 0; // use member variable.
00344 
00345   NS_ENSURE_TRUE(numBytesInBuffer > 0, NS_ERROR_FAILURE);
00346        
00347        //     if it is the first time, seek to the right start place. 
00348        if (mState == BINHEX_STATE_START)
00349        {
00350                foundStart = PR_FALSE;
00351               // go through the line, until we get a ':'
00352               while (mPosInDataBuffer < numBytesInBuffer)
00353               {
00354                      c = mDataBuffer[mPosInDataBuffer++];
00355                      while (c == CR || c == LF)
00356                      {
00357                             if (mPosInDataBuffer >= numBytesInBuffer)
00358                                    break;
00359                                                                                                                 
00360                             c = mDataBuffer[mPosInDataBuffer++];
00361                             if (c == ':')
00362                             {
00363                                    foundStart = PR_TRUE;
00364                                    break;
00365                             }
00366                      }
00367                      if (foundStart)      break;        /* we got the start point. */
00368               }
00369               
00370               if (mPosInDataBuffer >= numBytesInBuffer)
00371                      return NS_OK;               /* we meet buff end before we get the start point, wait till next fills. */
00372               
00373               if (c != ':')
00374                      return NS_ERROR_FAILURE;           /* can't find the start character. */
00375        }
00376        
00377        while (mState != BINHEX_STATE_DONE) 
00378        {
00379               /* fill in octetbuf */
00380               do 
00381               {
00382                      if (mPosInDataBuffer >= numBytesInBuffer)
00383                             return NS_OK;               /* end of buff, go on for the nxet calls. */
00384                                    
00385                      c = GetNextChar(numBytesInBuffer);
00386                      if (c == 0)   return NS_OK;
00387                              
00388                      if ((val = BHEXVAL(c)) == PRUint32(-1))
00389                      {
00390                             /* we incount an invalid character.       */
00391                             if (c) 
00392                             {
00393                                    /* rolling back. */
00394                                    mDonePos --;
00395                                    if (mOctetin >= 14)         mDonePos--;
00396                                    if (mOctetin >= 20)  mDonePos--;
00397                             }
00398                             break;
00399                      }
00400                      mOctetBuf.val |= val << mOctetin;
00401               } 
00402               while ((mOctetin -= 6) > 2);
00403                      
00404               /* handle decoded characters -- run length encoding (rle) detection */
00405 
00406               // We put decoded chars into mOctetBuf.val in order from high to low (via
00407               // bitshifting, above).  But we want to byte-address them, so we want the
00408               // first byte to correspond to the high byte.  In other words, we want
00409               // these bytes to be in network order.
00410               mOctetBuf.val = PR_htonl(mOctetBuf.val);
00411 
00412               for (octetpos = 0; octetpos < mDonePos; ++octetpos) 
00413               {
00414                      c = mOctetBuf.c[octetpos];
00415                      
00416                      if (c == 0x90 && !mMarker++) 
00417                             continue;
00418                                           
00419                      if (mMarker) 
00420                      {
00421                             if (c == 0) 
00422                             {
00423                                    mRlebuf = 0x90;
00424                                    ProcessNextState(aRequest, aContext);
00425                             } 
00426                             else 
00427                             {
00428                                    while (--c > 0)                           /* we are in the run lenght mode */ 
00429                                           ProcessNextState(aRequest, aContext);
00430                             }
00431                             mMarker = 0;
00432                      } 
00433                      else 
00434                      {
00435                             mRlebuf = (unsigned char) c;
00436                             ProcessNextState(aRequest, aContext);
00437                      }             
00438                      
00439                      if (mState >= BINHEX_STATE_DONE) 
00440                             break;
00441               }
00442               
00443               /* prepare for next 3 characters.  */
00444               if (mDonePos < 3 && mState < BINHEX_STATE_DONE) 
00445                      mState = BINHEX_STATE_DONE;
00446                                    
00447               mOctetin = 26;
00448               mOctetBuf.val        = 0;
00449        }
00450 
00451   return      NS_OK;
00452 }
00453 
00454 PRInt16 nsBinHexDecoder::GetNextChar(PRUint32 numBytesInBuffer)
00455 {
00456        char c = 0;
00457        
00458        while (mPosInDataBuffer < numBytesInBuffer)
00459        {
00460               c = mDataBuffer[mPosInDataBuffer++];
00461               if (c != LF && c != CR)
00462                      break;
00463        }
00464        return (c == LF || c == CR) ? 0 : (int) c;
00465 }
00466 
00468 // nsIRequestObserver methods...
00470 
00471 NS_IMETHODIMP
00472 nsBinHexDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt) 
00473 {
00474   nsresult rv = NS_OK;
00475 
00476   NS_ENSURE_TRUE(mNextListener, NS_ERROR_FAILURE);
00477 
00478   mDataBuffer = (char *) nsMemory::Alloc((sizeof(char) * DATA_BUFFER_SIZE));
00479   mOutgoingBuffer = (char *) nsMemory::Alloc((sizeof(char) * DATA_BUFFER_SIZE));
00480   if (!mDataBuffer || !mOutgoingBuffer) return NS_ERROR_FAILURE; // out of memory;
00481 
00482   // now we want to create a pipe which we'll use to write our converted data...
00483   rv = NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream),
00484                   NS_STREAM_CONVERTER_SEGMENT_SIZE,
00485                   NS_STREAM_CONVERTER_BUFFER_SIZE,
00486                   PR_TRUE, PR_TRUE);
00487 
00488   // don't propagate the on start request to mNextListener until we have determined the content type.
00489   return rv;
00490 }
00491 
00492 // Given the fileName we discovered inside the bin hex decoding, figure out the
00493 // content type and set it on the channel associated with the request.  If the
00494 // filename tells us nothing useful, just report an unknown type and let the
00495 // unknown decoder handle things.
00496 nsresult nsBinHexDecoder::SetContentType(nsIRequest* aRequest,
00497                                          const char * fileName)
00498 {
00499   if (!fileName || !*fileName) {
00500     // Nothing to do here.
00501     return NS_OK;
00502   }
00503 
00504   nsresult rv;
00505   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest, &rv));
00506   NS_ENSURE_SUCCESS(rv, rv);
00507   
00508   nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
00509   NS_ENSURE_SUCCESS(rv, rv);
00510 
00511   nsCAutoString contentType;
00512 
00513   // extract the extension from fileName and look it up.
00514   const char * fileExt = strrchr(fileName, '.');
00515   if (!fileExt) {
00516     return NS_OK;
00517   }
00518 
00519   mimeService->GetTypeFromExtension(nsDependentCString(fileExt), contentType);
00520 
00521   // Only set the type if it's not empty and, to prevent recursive loops, not the binhex type
00522   if (!contentType.IsEmpty() && !contentType.Equals(APPLICATION_BINHEX)) {
00523     channel->SetContentType(contentType);
00524   } else {
00525     channel->SetContentType(NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
00526   }
00527 
00528   return NS_OK;
00529 }
00530 
00531 
00532 NS_IMETHODIMP
00533 nsBinHexDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
00534                                 nsresult aStatus)
00535 {
00536   nsresult rv = NS_OK;
00537 
00538   if (!mNextListener) return NS_ERROR_FAILURE;
00539   // don't do anything here...we'll fire our own on stop request when we are done
00540   // processing the data....
00541 
00542   return rv;
00543 }