|
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 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; |
|
30 |
|
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 } |
|
38 |
|
39 nsBinHexDecoder::~nsBinHexDecoder() |
|
40 { |
|
41 if (mDataBuffer) |
|
42 nsMemory::Free(mDataBuffer); |
|
43 if (mOutgoingBuffer) |
|
44 nsMemory::Free(mOutgoingBuffer); |
|
45 } |
|
46 |
|
47 NS_IMPL_ADDREF(nsBinHexDecoder) |
|
48 NS_IMPL_RELEASE(nsBinHexDecoder) |
|
49 |
|
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 |
|
56 |
|
57 |
|
58 // The binhex 4.0 decoder table.... |
|
59 |
|
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 }; |
|
79 |
|
80 #define BHEXVAL(c) (binhex_decode[(unsigned char) c]) |
|
81 |
|
82 ////////////////////////////////////////////////////// |
|
83 // nsIStreamConverter methods... |
|
84 ////////////////////////////////////////////////////// |
|
85 |
|
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 } |
|
95 |
|
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"); |
|
104 |
|
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 } |
|
111 |
|
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; |
|
123 |
|
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; |
|
134 |
|
135 // Process this new chunk of bin hex data... |
|
136 ProcessNextChunk(request, aCtxt, numBytesRead); |
|
137 } |
|
138 } |
|
139 |
|
140 return rv; |
|
141 } |
|
142 |
|
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; |
|
148 |
|
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)); |
|
155 |
|
156 /* handle state */ |
|
157 switch (mState) |
|
158 { |
|
159 case BINHEX_STATE_START: |
|
160 mState = BINHEX_STATE_FNAME; |
|
161 mCount = 0; |
|
162 |
|
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; |
|
171 |
|
172 case BINHEX_STATE_FNAME: |
|
173 mName.BeginWriting()[mCount] = c; |
|
174 |
|
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.... |
|
179 |
|
180 DetectContentType(aRequest, mName); |
|
181 // now propagate the on start request |
|
182 mNextListener->OnStartRequest(aRequest, aContext); |
|
183 |
|
184 mState = BINHEX_STATE_HEADER; |
|
185 mCount = 0; |
|
186 } |
|
187 break; |
|
188 |
|
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 } |
|
203 |
|
204 mState = BINHEX_STATE_HCRC; |
|
205 mInCRC = 1; |
|
206 mCount = 0; |
|
207 } |
|
208 break; |
|
209 |
|
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; |
|
222 |
|
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. */ |
|
228 |
|
229 mPosOutputBuff = 0; |
|
230 |
|
231 if (status != NS_OK) |
|
232 mState = BINHEX_STATE_DONE; |
|
233 else |
|
234 ++mState; |
|
235 |
|
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; |
|
246 |
|
247 mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten); |
|
248 mPosOutputBuff = 0; |
|
249 } |
|
250 } |
|
251 break; |
|
252 |
|
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 } |
|
265 |
|
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; |
|
273 |
|
274 /* now We are done with everything. */ |
|
275 ++mState; |
|
276 break; |
|
277 } |
|
278 |
|
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 } |
|
288 |
|
289 if (mCount) { |
|
290 mInCRC = 0; |
|
291 } else { |
|
292 /* nothing inside, so skip to the next state. */ |
|
293 ++mState; |
|
294 } |
|
295 } |
|
296 break; |
|
297 } |
|
298 |
|
299 return NS_OK; |
|
300 } |
|
301 |
|
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. |
|
308 |
|
309 NS_ENSURE_TRUE(numBytesInBuffer > 0, NS_ERROR_FAILURE); |
|
310 |
|
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; |
|
323 |
|
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 } |
|
333 |
|
334 if (mPosInDataBuffer >= numBytesInBuffer) |
|
335 return NS_OK; /* we meet buff end before we get the start point, wait till next fills. */ |
|
336 |
|
337 if (c != ':') |
|
338 return NS_ERROR_FAILURE; /* can't find the start character. */ |
|
339 } |
|
340 |
|
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. */ |
|
348 |
|
349 c = GetNextChar(numBytesInBuffer); |
|
350 if (c == 0) return NS_OK; |
|
351 |
|
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); |
|
369 |
|
370 /* handle decoded characters -- run length encoding (rle) detection */ |
|
371 |
|
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); |
|
377 |
|
378 for (octetpos = 0; octetpos < mDonePos; ++octetpos) |
|
379 { |
|
380 c = mOctetBuf.c[octetpos]; |
|
381 |
|
382 if (c == 0x90 && !mMarker++) |
|
383 continue; |
|
384 |
|
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 } |
|
405 |
|
406 if (mState >= BINHEX_STATE_DONE) |
|
407 break; |
|
408 } |
|
409 |
|
410 /* prepare for next 3 characters. */ |
|
411 if (mDonePos < 3 && mState < BINHEX_STATE_DONE) |
|
412 mState = BINHEX_STATE_DONE; |
|
413 |
|
414 mOctetin = 26; |
|
415 mOctetBuf.val = 0; |
|
416 } |
|
417 |
|
418 return NS_OK; |
|
419 } |
|
420 |
|
421 int16_t nsBinHexDecoder::GetNextChar(uint32_t numBytesInBuffer) |
|
422 { |
|
423 char c = 0; |
|
424 |
|
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 } |
|
433 |
|
434 ////////////////////////////////////////////////////// |
|
435 // nsIRequestObserver methods... |
|
436 ////////////////////////////////////////////////////// |
|
437 |
|
438 NS_IMETHODIMP |
|
439 nsBinHexDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt) |
|
440 { |
|
441 nsresult rv = NS_OK; |
|
442 |
|
443 NS_ENSURE_TRUE(mNextListener, NS_ERROR_FAILURE); |
|
444 |
|
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; |
|
448 |
|
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); |
|
454 |
|
455 // don't propagate the on start request to mNextListener until we have determined the content type. |
|
456 return rv; |
|
457 } |
|
458 |
|
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 } |
|
470 |
|
471 nsresult rv; |
|
472 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest, &rv)); |
|
473 NS_ENSURE_SUCCESS(rv, rv); |
|
474 |
|
475 nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv)); |
|
476 NS_ENSURE_SUCCESS(rv, rv); |
|
477 |
|
478 nsAutoCString contentType; |
|
479 |
|
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 } |
|
485 |
|
486 mimeService->GetTypeFromExtension(nsDependentCString(fileExt), contentType); |
|
487 |
|
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 } |
|
494 |
|
495 return NS_OK; |
|
496 } |
|
497 |
|
498 |
|
499 NS_IMETHODIMP |
|
500 nsBinHexDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt, |
|
501 nsresult aStatus) |
|
502 { |
|
503 nsresult rv = NS_OK; |
|
504 |
|
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.... |
|
508 |
|
509 return rv; |
|
510 } |