1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
23 * Scott MacGregor <mscott@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsBinHexDecoder.h"
40 #include "nsIServiceManager.h"
41 #include "nsIStreamConverterService.h"
44 #include "nsMimeTypes.h"
46 #include "nsXPIDLString.h"
51 #include "nsIMIMEService.h"
52 #include "nsMimeTypes.h"
55 #define DATA_BUFFER_SIZE (4096*2)
57 #define NS_STREAM_CONVERTER_SEGMENT_SIZE (4*1024)
58 #define NS_STREAM_CONVERTER_BUFFER_SIZE (32*1024)
60 // sadly I couldn't find char defintions for CR LF elsehwere in the code (they are defined as strings in nsCRT.h)
64 nsBinHexDecoder::nsBinHexDecoder() :
65 mState(0), mCRC(0), mFileCRC(0), mOctetin(26),
66 mDonePos(3), mInCRC(0), mCount(0), mMarker(0), mPosInbuff(0),
70 mOutgoingBuffer
= nsnull
;
80 nsBinHexDecoder::~nsBinHexDecoder()
83 nsMemory::Free(mDataBuffer
);
85 nsMemory::Free(mOutgoingBuffer
);
88 NS_IMPL_ADDREF(nsBinHexDecoder
)
89 NS_IMPL_RELEASE(nsBinHexDecoder
)
91 NS_INTERFACE_MAP_BEGIN(nsBinHexDecoder
)
92 NS_INTERFACE_MAP_ENTRY(nsIStreamConverter
)
93 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
94 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
95 NS_INTERFACE_MAP_ENTRY(nsISupports
)
99 // The binhex 4.0 decoder table....
101 static char binhex_decode
[256] =
103 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
104 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
105 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1,
106 13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1,
107 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1,
108 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1,
109 48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1,
110 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
111 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
112 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
113 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
114 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
115 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
116 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
117 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121 #define BHEXVAL(c) (binhex_decode[(unsigned char) c])
123 //////////////////////////////////////////////////////
124 // nsIStreamConverter methods...
125 //////////////////////////////////////////////////////
128 nsBinHexDecoder::Convert(nsIInputStream
*aFromStream
,
129 const char *aFromType
,
132 nsIInputStream
**aResultStream
)
134 return NS_ERROR_NOT_IMPLEMENTED
;
138 nsBinHexDecoder::AsyncConvertData(const char *aFromType
,
140 nsIStreamListener
*aListener
,
143 NS_ASSERTION(aListener
&& aFromType
&& aToType
,
144 "null pointer passed into bin hex converter");
146 // hook up our final listener. this guy gets the various On*() calls we want to throw
149 mNextListener
= aListener
;
150 return (aListener
) ? NS_OK
: NS_ERROR_FAILURE
;
153 //////////////////////////////////////////////////////
154 // nsIStreamListener methods...
155 //////////////////////////////////////////////////////
157 nsBinHexDecoder::OnDataAvailable(nsIRequest
* request
,
159 nsIInputStream
*aStream
,
160 PRUint32 aSourceOffset
,
165 if (mOutputStream
&& mDataBuffer
&& aCount
> 0)
167 PRUint32 numBytesRead
= 0;
168 while (aCount
> 0) // while we still have bytes to copy...
170 aStream
->Read(mDataBuffer
, PR_MIN(aCount
, DATA_BUFFER_SIZE
- 1), &numBytesRead
);
171 if (aCount
>= numBytesRead
)
172 aCount
-= numBytesRead
; // subtract off the number of bytes we just read
176 // Process this new chunk of bin hex data...
177 ProcessNextChunk(request
, aCtxt
, numBytesRead
);
184 nsresult
nsBinHexDecoder::ProcessNextState(nsIRequest
* aRequest
, nsISupports
* aContext
)
186 nsresult status
= NS_OK
;
187 PRUint16 tmpcrc
, cval
;
188 unsigned char ctmp
, c
= mRlebuf
;
191 ctmp
= mInCRC
? c
: 0;
192 cval
= mCRC
& 0xf000;
193 tmpcrc
= ((PRUint16
) (mCRC
<< 4) | (ctmp
>> 4)) ^ (cval
| (cval
>> 7) | (cval
>> 12));
194 cval
= tmpcrc
& 0xf000;
195 mCRC
= ((PRUint16
) (tmpcrc
<< 4) | (ctmp
& 0x0f)) ^ (cval
| (cval
>> 7) | (cval
>> 12));
200 case BINHEX_STATE_START
:
201 mState
= BINHEX_STATE_FNAME
;
204 // c & 63 returns the length of mName. So if we need the length, that's how
205 // you can figure it out...for now we are storing it in the first byte of mName
209 case BINHEX_STATE_FNAME
:
212 if (mCount
++ > *(mName
)) // the first byte of mName is the length...
214 // okay we've figured out the file name....set the content type on the channel
215 // based on the file name AND issue our delayed on start request....
216 // be sure to skip the first byte of mName which is the size of the file name
218 SetContentType(aRequest
, &mName
[1]);
219 // now propagate the on start request
220 mNextListener
->OnStartRequest(aRequest
, aContext
);
222 mState
= BINHEX_STATE_HEADER
;
227 case BINHEX_STATE_HEADER
:
228 ((char *) &mHeader
)[mCount
] = c
;
231 if (sizeof(binhex_header
) != 18) /* fix an alignment problem in some OSes */
233 char *p
= (char *)&mHeader
;
235 for (c
= 0; c
< 8; c
++)
241 mState
= BINHEX_STATE_HCRC
;
247 case BINHEX_STATE_DFORK
:
248 case BINHEX_STATE_RFORK
:
249 mOutgoingBuffer
[mPosOutputBuff
++] = c
;
252 /* only output data fork in the non-mac system. */
253 if (mState
== BINHEX_STATE_DFORK
)
255 PRUint32 numBytesWritten
= 0;
256 mOutputStream
->Write(mOutgoingBuffer
, mPosOutputBuff
, &numBytesWritten
);
257 if (PRInt32(numBytesWritten
) != mPosOutputBuff
)
258 status
= NS_ERROR_FAILURE
;
260 // now propagate the data we just wrote
261 mNextListener
->OnDataAvailable(aRequest
, aContext
, mInputStream
, 0, numBytesWritten
);
264 status
= NS_OK
; /* do nothing for resource fork. */
269 mState
= BINHEX_STATE_DONE
;
275 else if (mPosOutputBuff
>= DATA_BUFFER_SIZE
)
277 if (mState
== BINHEX_STATE_DFORK
)
279 PRUint32 numBytesWritten
= 0;
280 mOutputStream
->Write(mOutgoingBuffer
, mPosOutputBuff
, &numBytesWritten
);
281 if (PRInt32(numBytesWritten
) != mPosOutputBuff
)
282 status
= NS_ERROR_FAILURE
;
284 mNextListener
->OnDataAvailable(aRequest
, aContext
, mInputStream
, 0, numBytesWritten
);
290 case BINHEX_STATE_HCRC
:
291 case BINHEX_STATE_DCRC
:
292 case BINHEX_STATE_RCRC
:
294 mFileCRC
= (unsigned short) c
<< 8;
297 if ((mFileCRC
| c
) != mCRC
)
299 mState
= BINHEX_STATE_DONE
;
303 /* passed the CRC check!!!*/
305 if (++ mState
== BINHEX_STATE_FINISH
)
307 // when we reach the finished state...fire an on stop request on the event listener...
308 mNextListener
->OnStopRequest(aRequest
, aContext
, NS_OK
);
311 /* now We are done with everything. */
316 if (mState
== BINHEX_STATE_DFORK
)
317 mCount
= PR_ntohl(mHeader
.dlen
);
320 // we aren't processing the resurce Fork. uncomment this line if we make this converter
321 // smart enough to do this in the future.
322 // mCount = PR_ntohl(mHeader.rlen); /* it should in host byte order */
329 /* nothing inside, so skip to the next state. */
338 nsresult
nsBinHexDecoder::ProcessNextChunk(nsIRequest
* aRequest
, nsISupports
* aContext
, PRUint32 numBytesInBuffer
)
341 PRInt16 octetpos
, c
= 0;
343 mPosInDataBuffer
= 0; // use member variable.
345 NS_ENSURE_TRUE(numBytesInBuffer
> 0, NS_ERROR_FAILURE
);
347 // if it is the first time, seek to the right start place.
348 if (mState
== BINHEX_STATE_START
)
350 foundStart
= PR_FALSE
;
351 // go through the line, until we get a ':'
352 while (mPosInDataBuffer
< numBytesInBuffer
)
354 c
= mDataBuffer
[mPosInDataBuffer
++];
355 while (c
== CR
|| c
== LF
)
357 if (mPosInDataBuffer
>= numBytesInBuffer
)
360 c
= mDataBuffer
[mPosInDataBuffer
++];
363 foundStart
= PR_TRUE
;
367 if (foundStart
) break; /* we got the start point. */
370 if (mPosInDataBuffer
>= numBytesInBuffer
)
371 return NS_OK
; /* we meet buff end before we get the start point, wait till next fills. */
374 return NS_ERROR_FAILURE
; /* can't find the start character. */
377 while (mState
!= BINHEX_STATE_DONE
)
379 /* fill in octetbuf */
382 if (mPosInDataBuffer
>= numBytesInBuffer
)
383 return NS_OK
; /* end of buff, go on for the nxet calls. */
385 c
= GetNextChar(numBytesInBuffer
);
386 if (c
== 0) return NS_OK
;
388 if ((val
= BHEXVAL(c
)) == PRUint32(-1))
390 /* we incount an invalid character. */
395 if (mOctetin
>= 14) mDonePos
--;
396 if (mOctetin
>= 20) mDonePos
--;
400 mOctetBuf
.val
|= val
<< mOctetin
;
402 while ((mOctetin
-= 6) > 2);
404 /* handle decoded characters -- run length encoding (rle) detection */
406 // We put decoded chars into mOctetBuf.val in order from high to low (via
407 // bitshifting, above). But we want to byte-address them, so we want the
408 // first byte to correspond to the high byte. In other words, we want
409 // these bytes to be in network order.
410 mOctetBuf
.val
= PR_htonl(mOctetBuf
.val
);
412 for (octetpos
= 0; octetpos
< mDonePos
; ++octetpos
)
414 c
= mOctetBuf
.c
[octetpos
];
416 if (c
== 0x90 && !mMarker
++)
424 ProcessNextState(aRequest
, aContext
);
428 while (--c
> 0) /* we are in the run lenght mode */
429 ProcessNextState(aRequest
, aContext
);
435 mRlebuf
= (unsigned char) c
;
436 ProcessNextState(aRequest
, aContext
);
439 if (mState
>= BINHEX_STATE_DONE
)
443 /* prepare for next 3 characters. */
444 if (mDonePos
< 3 && mState
< BINHEX_STATE_DONE
)
445 mState
= BINHEX_STATE_DONE
;
454 PRInt16
nsBinHexDecoder::GetNextChar(PRUint32 numBytesInBuffer
)
458 while (mPosInDataBuffer
< numBytesInBuffer
)
460 c
= mDataBuffer
[mPosInDataBuffer
++];
461 if (c
!= LF
&& c
!= CR
)
464 return (c
== LF
|| c
== CR
) ? 0 : (int) c
;
467 //////////////////////////////////////////////////////
468 // nsIRequestObserver methods...
469 //////////////////////////////////////////////////////
472 nsBinHexDecoder::OnStartRequest(nsIRequest
* request
, nsISupports
*aCtxt
)
476 NS_ENSURE_TRUE(mNextListener
, NS_ERROR_FAILURE
);
478 mDataBuffer
= (char *) nsMemory::Alloc((sizeof(char) * DATA_BUFFER_SIZE
));
479 mOutgoingBuffer
= (char *) nsMemory::Alloc((sizeof(char) * DATA_BUFFER_SIZE
));
480 if (!mDataBuffer
|| !mOutgoingBuffer
) return NS_ERROR_FAILURE
; // out of memory;
482 // now we want to create a pipe which we'll use to write our converted data...
483 rv
= NS_NewPipe(getter_AddRefs(mInputStream
), getter_AddRefs(mOutputStream
),
484 NS_STREAM_CONVERTER_SEGMENT_SIZE
,
485 NS_STREAM_CONVERTER_BUFFER_SIZE
,
488 // don't propagate the on start request to mNextListener until we have determined the content type.
492 // Given the fileName we discovered inside the bin hex decoding, figure out the
493 // content type and set it on the channel associated with the request. If the
494 // filename tells us nothing useful, just report an unknown type and let the
495 // unknown decoder handle things.
496 nsresult
nsBinHexDecoder::SetContentType(nsIRequest
* aRequest
,
497 const char * fileName
)
499 if (!fileName
|| !*fileName
) {
500 // Nothing to do here.
505 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
, &rv
));
506 NS_ENSURE_SUCCESS(rv
, rv
);
508 nsCOMPtr
<nsIMIMEService
> mimeService(do_GetService("@mozilla.org/mime;1", &rv
));
509 NS_ENSURE_SUCCESS(rv
, rv
);
511 nsCAutoString contentType
;
513 // extract the extension from fileName and look it up.
514 const char * fileExt
= strrchr(fileName
, '.');
519 mimeService
->GetTypeFromExtension(nsDependentCString(fileExt
), contentType
);
521 // Only set the type if it's not empty and, to prevent recursive loops, not the binhex type
522 if (!contentType
.IsEmpty() && !contentType
.Equals(APPLICATION_BINHEX
)) {
523 channel
->SetContentType(contentType
);
525 channel
->SetContentType(NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE
));
533 nsBinHexDecoder::OnStopRequest(nsIRequest
* request
, nsISupports
*aCtxt
,
538 if (!mNextListener
) return NS_ERROR_FAILURE
;
539 // don't do anything here...we'll fire our own on stop request when we are done
540 // processing the data....