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