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