Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / netwerk / streamconv / converters / nsBinHexDecoder.cpp
blob4c7442cd7b16c96637fe64ebb3cc5e853dc918d5
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
13 * License.
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.
22 * Contributor(s):
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"
42 #include "nsCRT.h"
43 #include "nsIPipe.h"
44 #include "nsMimeTypes.h"
45 #include "netCore.h"
46 #include "nsXPIDLString.h"
47 #include "prnetdb.h"
48 #include "nsIURI.h"
49 #include "nsIURL.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)
61 #define CR '\015'
62 #define LF '\012'
64 nsBinHexDecoder::nsBinHexDecoder() :
65 mState(0), mCRC(0), mFileCRC(0), mOctetin(26),
66 mDonePos(3), mInCRC(0), mCount(0), mMarker(0), mPosInbuff(0),
67 mPosOutputBuff(0)
69 mDataBuffer = nsnull;
70 mOutgoingBuffer = nsnull;
72 mOctetBuf.val = 0;
73 mHeader.type = 0;
74 mHeader.creator = 0;
75 mHeader.flags = 0;
76 mHeader.dlen = 0;
77 mHeader.rlen = 0;
80 nsBinHexDecoder::~nsBinHexDecoder()
82 if (mDataBuffer)
83 nsMemory::Free(mDataBuffer);
84 if (mOutgoingBuffer)
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)
96 NS_INTERFACE_MAP_END
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 //////////////////////////////////////////////////////
127 NS_IMETHODIMP
128 nsBinHexDecoder::Convert(nsIInputStream *aFromStream,
129 const char *aFromType,
130 const char *aToType,
131 nsISupports *aCtxt,
132 nsIInputStream **aResultStream)
134 return NS_ERROR_NOT_IMPLEMENTED;
137 NS_IMETHODIMP
138 nsBinHexDecoder::AsyncConvertData(const char *aFromType,
139 const char *aToType,
140 nsIStreamListener *aListener,
141 nsISupports *aCtxt)
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
147 // at him.
149 mNextListener = aListener;
150 return (aListener) ? NS_OK : NS_ERROR_FAILURE;
153 //////////////////////////////////////////////////////
154 // nsIStreamListener methods...
155 //////////////////////////////////////////////////////
156 NS_IMETHODIMP
157 nsBinHexDecoder::OnDataAvailable(nsIRequest* request,
158 nsISupports *aCtxt,
159 nsIInputStream *aStream,
160 PRUint32 aSourceOffset,
161 PRUint32 aCount)
163 nsresult rv = NS_OK;
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
173 else
174 aCount = 0;
176 // Process this new chunk of bin hex data...
177 ProcessNextChunk(request, aCtxt, numBytesRead);
181 return rv;
184 nsresult nsBinHexDecoder::ProcessNextState(nsIRequest * aRequest, nsISupports * aContext)
186 nsresult status = NS_OK;
187 PRUint16 tmpcrc, cval;
188 unsigned char ctmp, c = mRlebuf;
190 /* do CRC */
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));
197 /* handle state */
198 switch (mState)
200 case BINHEX_STATE_START:
201 mState = BINHEX_STATE_FNAME;
202 mCount = 1;
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
206 *(mName) = (c & 63);
207 break;
209 case BINHEX_STATE_FNAME:
210 mName[mCount] = c;
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;
223 mCount = 0;
225 break;
227 case BINHEX_STATE_HEADER:
228 ((char *) &mHeader)[mCount] = c;
229 if (++mCount == 18)
231 if (sizeof(binhex_header) != 18) /* fix an alignment problem in some OSes */
233 char *p = (char *)&mHeader;
234 p += 19;
235 for (c = 0; c < 8; c++)
237 *p = *(p-2); p--;
241 mState = BINHEX_STATE_HCRC;
242 mInCRC = 1;
243 mCount = 0;
245 break;
247 case BINHEX_STATE_DFORK:
248 case BINHEX_STATE_RFORK:
249 mOutgoingBuffer[mPosOutputBuff++] = c;
250 if (-- mCount == 0)
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);
263 else
264 status = NS_OK; /* do nothing for resource fork. */
266 mPosOutputBuff = 0;
268 if (status != NS_OK)
269 mState = BINHEX_STATE_DONE;
270 else
271 mState ++;
273 mInCRC = 1;
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);
285 mPosOutputBuff = 0;
288 break;
290 case BINHEX_STATE_HCRC:
291 case BINHEX_STATE_DCRC:
292 case BINHEX_STATE_RCRC:
293 if (!mCount++)
294 mFileCRC = (unsigned short) c << 8;
295 else
297 if ((mFileCRC | c) != mCRC)
299 mState = BINHEX_STATE_DONE;
300 break;
303 /* passed the CRC check!!!*/
304 mCRC = 0;
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);
309 mNextListener = 0;
311 /* now We are done with everything. */
312 mState++;
313 break;
316 if (mState == BINHEX_STATE_DFORK)
317 mCount = PR_ntohl(mHeader.dlen);
318 else
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 mCount = 0;
326 if (mCount)
327 mInCRC = 0;
328 else
329 /* nothing inside, so skip to the next state. */
330 mState ++;
332 break;
335 return NS_OK;
338 nsresult nsBinHexDecoder::ProcessNextChunk(nsIRequest * aRequest, nsISupports * aContext, PRUint32 numBytesInBuffer)
340 PRBool foundStart;
341 PRInt16 octetpos, c = 0;
342 PRUint32 val;
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)
358 break;
360 c = mDataBuffer[mPosInDataBuffer++];
361 if (c == ':')
363 foundStart = PR_TRUE;
364 break;
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. */
373 if (c != ':')
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. */
391 if (c)
393 /* rolling back. */
394 mDonePos --;
395 if (mOctetin >= 14) mDonePos--;
396 if (mOctetin >= 20) mDonePos--;
398 break;
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++)
417 continue;
419 if (mMarker)
421 if (c == 0)
423 mRlebuf = 0x90;
424 ProcessNextState(aRequest, aContext);
426 else
428 while (--c > 0) /* we are in the run lenght mode */
429 ProcessNextState(aRequest, aContext);
431 mMarker = 0;
433 else
435 mRlebuf = (unsigned char) c;
436 ProcessNextState(aRequest, aContext);
439 if (mState >= BINHEX_STATE_DONE)
440 break;
443 /* prepare for next 3 characters. */
444 if (mDonePos < 3 && mState < BINHEX_STATE_DONE)
445 mState = BINHEX_STATE_DONE;
447 mOctetin = 26;
448 mOctetBuf.val = 0;
451 return NS_OK;
454 PRInt16 nsBinHexDecoder::GetNextChar(PRUint32 numBytesInBuffer)
456 char c = 0;
458 while (mPosInDataBuffer < numBytesInBuffer)
460 c = mDataBuffer[mPosInDataBuffer++];
461 if (c != LF && c != CR)
462 break;
464 return (c == LF || c == CR) ? 0 : (int) c;
467 //////////////////////////////////////////////////////
468 // nsIRequestObserver methods...
469 //////////////////////////////////////////////////////
471 NS_IMETHODIMP
472 nsBinHexDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt)
474 nsresult rv = NS_OK;
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,
486 PR_TRUE, PR_TRUE);
488 // don't propagate the on start request to mNextListener until we have determined the content type.
489 return rv;
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.
501 return NS_OK;
504 nsresult rv;
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, '.');
515 if (!fileExt) {
516 return NS_OK;
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);
524 } else {
525 channel->SetContentType(NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
528 return NS_OK;
532 NS_IMETHODIMP
533 nsBinHexDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
534 nsresult aStatus)
536 nsresult rv = NS_OK;
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....
542 return rv;