Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
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 }