1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsBinHexDecoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,510 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsIOService.h" 1.10 +#include "nsBinHexDecoder.h" 1.11 +#include "nsIServiceManager.h" 1.12 +#include "nsIStreamConverterService.h" 1.13 +#include "nsCRT.h" 1.14 +#include "nsIPipe.h" 1.15 +#include "nsMimeTypes.h" 1.16 +#include "netCore.h" 1.17 +#include "nsXPIDLString.h" 1.18 +#include "prnetdb.h" 1.19 +#include "nsIURI.h" 1.20 +#include "nsIURL.h" 1.21 + 1.22 +#include "nsIMIMEService.h" 1.23 +#include "nsMimeTypes.h" 1.24 +#include <algorithm> 1.25 + 1.26 +nsBinHexDecoder::nsBinHexDecoder() : 1.27 + mState(0), mCRC(0), mFileCRC(0), mOctetin(26), 1.28 + mDonePos(3), mInCRC(0), mCount(0), mMarker(0), mPosInbuff(0), 1.29 + mPosOutputBuff(0) 1.30 +{ 1.31 + mDataBuffer = nullptr; 1.32 + mOutgoingBuffer = nullptr; 1.33 + 1.34 + mOctetBuf.val = 0; 1.35 + mHeader.type = 0; 1.36 + mHeader.creator = 0; 1.37 + mHeader.flags = 0; 1.38 + mHeader.dlen = 0; 1.39 + mHeader.rlen = 0; 1.40 +} 1.41 + 1.42 +nsBinHexDecoder::~nsBinHexDecoder() 1.43 +{ 1.44 + if (mDataBuffer) 1.45 + nsMemory::Free(mDataBuffer); 1.46 + if (mOutgoingBuffer) 1.47 + nsMemory::Free(mOutgoingBuffer); 1.48 +} 1.49 + 1.50 +NS_IMPL_ADDREF(nsBinHexDecoder) 1.51 +NS_IMPL_RELEASE(nsBinHexDecoder) 1.52 + 1.53 +NS_INTERFACE_MAP_BEGIN(nsBinHexDecoder) 1.54 + NS_INTERFACE_MAP_ENTRY(nsIStreamConverter) 1.55 + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1.56 + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1.57 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.58 +NS_INTERFACE_MAP_END 1.59 + 1.60 + 1.61 +// The binhex 4.0 decoder table.... 1.62 + 1.63 +static const signed char binhex_decode[256] = 1.64 +{ 1.65 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.66 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.67 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, 1.68 + 13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1, 1.69 + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, 1.70 + 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1, 1.71 + 48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1, 1.72 + 61, 62, 63, -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, 1.74 + -1, -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, 1.76 + -1, -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, 1.78 + -1, -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, 1.80 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.81 +}; 1.82 + 1.83 +#define BHEXVAL(c) (binhex_decode[(unsigned char) c]) 1.84 + 1.85 +////////////////////////////////////////////////////// 1.86 +// nsIStreamConverter methods... 1.87 +////////////////////////////////////////////////////// 1.88 + 1.89 +NS_IMETHODIMP 1.90 +nsBinHexDecoder::Convert(nsIInputStream *aFromStream, 1.91 + const char *aFromType, 1.92 + const char *aToType, 1.93 + nsISupports *aCtxt, 1.94 + nsIInputStream **aResultStream) 1.95 +{ 1.96 + return NS_ERROR_NOT_IMPLEMENTED; 1.97 +} 1.98 + 1.99 +NS_IMETHODIMP 1.100 +nsBinHexDecoder::AsyncConvertData(const char *aFromType, 1.101 + const char *aToType, 1.102 + nsIStreamListener *aListener, 1.103 + nsISupports *aCtxt) 1.104 +{ 1.105 + NS_ASSERTION(aListener && aFromType && aToType, 1.106 + "null pointer passed into bin hex converter"); 1.107 + 1.108 + // hook up our final listener. this guy gets the various On*() calls we want to throw 1.109 + // at him. 1.110 + // 1.111 + mNextListener = aListener; 1.112 + return (aListener) ? NS_OK : NS_ERROR_FAILURE; 1.113 +} 1.114 + 1.115 +////////////////////////////////////////////////////// 1.116 +// nsIStreamListener methods... 1.117 +////////////////////////////////////////////////////// 1.118 +NS_IMETHODIMP 1.119 +nsBinHexDecoder::OnDataAvailable(nsIRequest* request, 1.120 + nsISupports *aCtxt, 1.121 + nsIInputStream *aStream, 1.122 + uint64_t aSourceOffset, 1.123 + uint32_t aCount) 1.124 +{ 1.125 + nsresult rv = NS_OK; 1.126 + 1.127 + if (mOutputStream && mDataBuffer && aCount > 0) 1.128 + { 1.129 + uint32_t numBytesRead = 0; 1.130 + while (aCount > 0) // while we still have bytes to copy... 1.131 + { 1.132 + aStream->Read(mDataBuffer, std::min(aCount, nsIOService::gDefaultSegmentSize - 1), &numBytesRead); 1.133 + if (aCount >= numBytesRead) 1.134 + aCount -= numBytesRead; // subtract off the number of bytes we just read 1.135 + else 1.136 + aCount = 0; 1.137 + 1.138 + // Process this new chunk of bin hex data... 1.139 + ProcessNextChunk(request, aCtxt, numBytesRead); 1.140 + } 1.141 + } 1.142 + 1.143 + return rv; 1.144 +} 1.145 + 1.146 +nsresult nsBinHexDecoder::ProcessNextState(nsIRequest * aRequest, nsISupports * aContext) 1.147 +{ 1.148 + nsresult status = NS_OK; 1.149 + uint16_t tmpcrc, cval; 1.150 + unsigned char ctmp, c = mRlebuf; 1.151 + 1.152 + /* do CRC */ 1.153 + ctmp = mInCRC ? c : 0; 1.154 + cval = mCRC & 0xf000; 1.155 + tmpcrc = ((uint16_t) (mCRC << 4) | (ctmp >> 4)) ^ (cval | (cval >> 7) | (cval >> 12)); 1.156 + cval = tmpcrc & 0xf000; 1.157 + mCRC = ((uint16_t) (tmpcrc << 4) | (ctmp & 0x0f)) ^ (cval | (cval >> 7) | (cval >> 12)); 1.158 + 1.159 + /* handle state */ 1.160 + switch (mState) 1.161 + { 1.162 + case BINHEX_STATE_START: 1.163 + mState = BINHEX_STATE_FNAME; 1.164 + mCount = 0; 1.165 + 1.166 + // c & 63 returns the length of mName. So if we need the length, that's how 1.167 + // you can figure it out.... 1.168 + mName.SetLength(c & 63); 1.169 + if (mName.Length() != (c & 63)) { 1.170 + /* XXX ProcessNextState/ProcessNextChunk aren't rv checked */ 1.171 + mState = BINHEX_STATE_DONE; 1.172 + } 1.173 + break; 1.174 + 1.175 + case BINHEX_STATE_FNAME: 1.176 + mName.BeginWriting()[mCount] = c; 1.177 + 1.178 + if (++mCount > mName.Length()) 1.179 + { 1.180 + // okay we've figured out the file name....set the content type on the channel 1.181 + // based on the file name AND issue our delayed on start request.... 1.182 + 1.183 + DetectContentType(aRequest, mName); 1.184 + // now propagate the on start request 1.185 + mNextListener->OnStartRequest(aRequest, aContext); 1.186 + 1.187 + mState = BINHEX_STATE_HEADER; 1.188 + mCount = 0; 1.189 + } 1.190 + break; 1.191 + 1.192 + case BINHEX_STATE_HEADER: 1.193 + ((char *) &mHeader)[mCount] = c; 1.194 + if (++mCount == 18) 1.195 + { 1.196 + if (sizeof(binhex_header) != 18) /* fix an alignment problem in some OSes */ 1.197 + { 1.198 + char *p = (char *)&mHeader; 1.199 + p += 19; 1.200 + for (c = 0; c < 8; c++) 1.201 + { 1.202 + *p = *(p-2); 1.203 + --p; 1.204 + } 1.205 + } 1.206 + 1.207 + mState = BINHEX_STATE_HCRC; 1.208 + mInCRC = 1; 1.209 + mCount = 0; 1.210 + } 1.211 + break; 1.212 + 1.213 + case BINHEX_STATE_DFORK: 1.214 + case BINHEX_STATE_RFORK: 1.215 + mOutgoingBuffer[mPosOutputBuff++] = c; 1.216 + if (--mCount == 0) 1.217 + { 1.218 + /* only output data fork in the non-mac system. */ 1.219 + if (mState == BINHEX_STATE_DFORK) 1.220 + { 1.221 + uint32_t numBytesWritten = 0; 1.222 + mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten); 1.223 + if (int32_t(numBytesWritten) != mPosOutputBuff) 1.224 + status = NS_ERROR_FAILURE; 1.225 + 1.226 + // now propagate the data we just wrote 1.227 + mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten); 1.228 + } 1.229 + else 1.230 + status = NS_OK; /* do nothing for resource fork. */ 1.231 + 1.232 + mPosOutputBuff = 0; 1.233 + 1.234 + if (status != NS_OK) 1.235 + mState = BINHEX_STATE_DONE; 1.236 + else 1.237 + ++mState; 1.238 + 1.239 + mInCRC = 1; 1.240 + } 1.241 + else if (mPosOutputBuff >= (int32_t) nsIOService::gDefaultSegmentSize) 1.242 + { 1.243 + if (mState == BINHEX_STATE_DFORK) 1.244 + { 1.245 + uint32_t numBytesWritten = 0; 1.246 + mOutputStream->Write(mOutgoingBuffer, mPosOutputBuff, &numBytesWritten); 1.247 + if (int32_t(numBytesWritten) != mPosOutputBuff) 1.248 + status = NS_ERROR_FAILURE; 1.249 + 1.250 + mNextListener->OnDataAvailable(aRequest, aContext, mInputStream, 0, numBytesWritten); 1.251 + mPosOutputBuff = 0; 1.252 + } 1.253 + } 1.254 + break; 1.255 + 1.256 + case BINHEX_STATE_HCRC: 1.257 + case BINHEX_STATE_DCRC: 1.258 + case BINHEX_STATE_RCRC: 1.259 + if (!mCount++) 1.260 + mFileCRC = (unsigned short) c << 8; 1.261 + else 1.262 + { 1.263 + if ((mFileCRC | c) != mCRC) 1.264 + { 1.265 + mState = BINHEX_STATE_DONE; 1.266 + break; 1.267 + } 1.268 + 1.269 + /* passed the CRC check!!!*/ 1.270 + mCRC = 0; 1.271 + if (++mState == BINHEX_STATE_FINISH) 1.272 + { 1.273 + // when we reach the finished state...fire an on stop request on the event listener... 1.274 + mNextListener->OnStopRequest(aRequest, aContext, NS_OK); 1.275 + mNextListener = 0; 1.276 + 1.277 + /* now We are done with everything. */ 1.278 + ++mState; 1.279 + break; 1.280 + } 1.281 + 1.282 + if (mState == BINHEX_STATE_DFORK) 1.283 + mCount = PR_ntohl(mHeader.dlen); 1.284 + else 1.285 + { 1.286 + // we aren't processing the resurce Fork. uncomment this line if we make this converter 1.287 + // smart enough to do this in the future. 1.288 + // mCount = PR_ntohl(mHeader.rlen); /* it should in host byte order */ 1.289 + mCount = 0; 1.290 + } 1.291 + 1.292 + if (mCount) { 1.293 + mInCRC = 0; 1.294 + } else { 1.295 + /* nothing inside, so skip to the next state. */ 1.296 + ++mState; 1.297 + } 1.298 + } 1.299 + break; 1.300 + } 1.301 + 1.302 + return NS_OK; 1.303 +} 1.304 + 1.305 +nsresult nsBinHexDecoder::ProcessNextChunk(nsIRequest * aRequest, nsISupports * aContext, uint32_t numBytesInBuffer) 1.306 +{ 1.307 + bool foundStart; 1.308 + int16_t octetpos, c = 0; 1.309 + uint32_t val; 1.310 + mPosInDataBuffer = 0; // use member variable. 1.311 + 1.312 + NS_ENSURE_TRUE(numBytesInBuffer > 0, NS_ERROR_FAILURE); 1.313 + 1.314 + // if it is the first time, seek to the right start place. 1.315 + if (mState == BINHEX_STATE_START) 1.316 + { 1.317 + foundStart = false; 1.318 + // go through the line, until we get a ':' 1.319 + while (mPosInDataBuffer < numBytesInBuffer) 1.320 + { 1.321 + c = mDataBuffer[mPosInDataBuffer++]; 1.322 + while (c == nsCRT::CR || c == nsCRT::LF) 1.323 + { 1.324 + if (mPosInDataBuffer >= numBytesInBuffer) 1.325 + break; 1.326 + 1.327 + c = mDataBuffer[mPosInDataBuffer++]; 1.328 + if (c == ':') 1.329 + { 1.330 + foundStart = true; 1.331 + break; 1.332 + } 1.333 + } 1.334 + if (foundStart) break; /* we got the start point. */ 1.335 + } 1.336 + 1.337 + if (mPosInDataBuffer >= numBytesInBuffer) 1.338 + return NS_OK; /* we meet buff end before we get the start point, wait till next fills. */ 1.339 + 1.340 + if (c != ':') 1.341 + return NS_ERROR_FAILURE; /* can't find the start character. */ 1.342 + } 1.343 + 1.344 + while (mState != BINHEX_STATE_DONE) 1.345 + { 1.346 + /* fill in octetbuf */ 1.347 + do 1.348 + { 1.349 + if (mPosInDataBuffer >= numBytesInBuffer) 1.350 + return NS_OK; /* end of buff, go on for the nxet calls. */ 1.351 + 1.352 + c = GetNextChar(numBytesInBuffer); 1.353 + if (c == 0) return NS_OK; 1.354 + 1.355 + if ((val = BHEXVAL(c)) == uint32_t(-1)) 1.356 + { 1.357 + /* we incount an invalid character. */ 1.358 + if (c) 1.359 + { 1.360 + /* rolling back. */ 1.361 + --mDonePos; 1.362 + if (mOctetin >= 14) 1.363 + --mDonePos; 1.364 + if (mOctetin >= 20) 1.365 + --mDonePos; 1.366 + } 1.367 + break; 1.368 + } 1.369 + mOctetBuf.val |= val << mOctetin; 1.370 + } 1.371 + while ((mOctetin -= 6) > 2); 1.372 + 1.373 + /* handle decoded characters -- run length encoding (rle) detection */ 1.374 + 1.375 + // We put decoded chars into mOctetBuf.val in order from high to low (via 1.376 + // bitshifting, above). But we want to byte-address them, so we want the 1.377 + // first byte to correspond to the high byte. In other words, we want 1.378 + // these bytes to be in network order. 1.379 + mOctetBuf.val = PR_htonl(mOctetBuf.val); 1.380 + 1.381 + for (octetpos = 0; octetpos < mDonePos; ++octetpos) 1.382 + { 1.383 + c = mOctetBuf.c[octetpos]; 1.384 + 1.385 + if (c == 0x90 && !mMarker++) 1.386 + continue; 1.387 + 1.388 + if (mMarker) 1.389 + { 1.390 + if (c == 0) 1.391 + { 1.392 + mRlebuf = 0x90; 1.393 + ProcessNextState(aRequest, aContext); 1.394 + } 1.395 + else 1.396 + { 1.397 + /* we are in the run length mode */ 1.398 + while (--c > 0) 1.399 + ProcessNextState(aRequest, aContext); 1.400 + } 1.401 + mMarker = 0; 1.402 + } 1.403 + else 1.404 + { 1.405 + mRlebuf = (unsigned char) c; 1.406 + ProcessNextState(aRequest, aContext); 1.407 + } 1.408 + 1.409 + if (mState >= BINHEX_STATE_DONE) 1.410 + break; 1.411 + } 1.412 + 1.413 + /* prepare for next 3 characters. */ 1.414 + if (mDonePos < 3 && mState < BINHEX_STATE_DONE) 1.415 + mState = BINHEX_STATE_DONE; 1.416 + 1.417 + mOctetin = 26; 1.418 + mOctetBuf.val = 0; 1.419 + } 1.420 + 1.421 + return NS_OK; 1.422 +} 1.423 + 1.424 +int16_t nsBinHexDecoder::GetNextChar(uint32_t numBytesInBuffer) 1.425 +{ 1.426 + char c = 0; 1.427 + 1.428 + while (mPosInDataBuffer < numBytesInBuffer) 1.429 + { 1.430 + c = mDataBuffer[mPosInDataBuffer++]; 1.431 + if (c != nsCRT::LF && c != nsCRT::CR) 1.432 + break; 1.433 + } 1.434 + return (c == nsCRT::LF || c == nsCRT::CR) ? 0 : (int) c; 1.435 +} 1.436 + 1.437 +////////////////////////////////////////////////////// 1.438 +// nsIRequestObserver methods... 1.439 +////////////////////////////////////////////////////// 1.440 + 1.441 +NS_IMETHODIMP 1.442 +nsBinHexDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt) 1.443 +{ 1.444 + nsresult rv = NS_OK; 1.445 + 1.446 + NS_ENSURE_TRUE(mNextListener, NS_ERROR_FAILURE); 1.447 + 1.448 + mDataBuffer = (char *) moz_malloc((sizeof(char) * nsIOService::gDefaultSegmentSize)); 1.449 + mOutgoingBuffer = (char *) moz_malloc((sizeof(char) * nsIOService::gDefaultSegmentSize)); 1.450 + if (!mDataBuffer || !mOutgoingBuffer) return NS_ERROR_FAILURE; // out of memory; 1.451 + 1.452 + // now we want to create a pipe which we'll use to write our converted data... 1.453 + rv = NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream), 1.454 + nsIOService::gDefaultSegmentSize, 1.455 + nsIOService::gDefaultSegmentSize, 1.456 + true, true); 1.457 + 1.458 + // don't propagate the on start request to mNextListener until we have determined the content type. 1.459 + return rv; 1.460 +} 1.461 + 1.462 +// Given the fileName we discovered inside the bin hex decoding, figure out the 1.463 +// content type and set it on the channel associated with the request. If the 1.464 +// filename tells us nothing useful, just report an unknown type and let the 1.465 +// unknown decoder handle things. 1.466 +nsresult nsBinHexDecoder::DetectContentType(nsIRequest* aRequest, 1.467 + const nsAFlatCString &aFilename) 1.468 +{ 1.469 + if (aFilename.IsEmpty()) { 1.470 + // Nothing to do here. 1.471 + return NS_OK; 1.472 + } 1.473 + 1.474 + nsresult rv; 1.475 + nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest, &rv)); 1.476 + NS_ENSURE_SUCCESS(rv, rv); 1.477 + 1.478 + nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv)); 1.479 + NS_ENSURE_SUCCESS(rv, rv); 1.480 + 1.481 + nsAutoCString contentType; 1.482 + 1.483 + // extract the extension from aFilename and look it up. 1.484 + const char * fileExt = strrchr(aFilename.get(), '.'); 1.485 + if (!fileExt) { 1.486 + return NS_OK; 1.487 + } 1.488 + 1.489 + mimeService->GetTypeFromExtension(nsDependentCString(fileExt), contentType); 1.490 + 1.491 + // Only set the type if it's not empty and, to prevent recursive loops, not the binhex type 1.492 + if (!contentType.IsEmpty() && !contentType.Equals(APPLICATION_BINHEX)) { 1.493 + channel->SetContentType(contentType); 1.494 + } else { 1.495 + channel->SetContentType(NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE)); 1.496 + } 1.497 + 1.498 + return NS_OK; 1.499 +} 1.500 + 1.501 + 1.502 +NS_IMETHODIMP 1.503 +nsBinHexDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt, 1.504 + nsresult aStatus) 1.505 +{ 1.506 + nsresult rv = NS_OK; 1.507 + 1.508 + if (!mNextListener) return NS_ERROR_FAILURE; 1.509 + // don't do anything here...we'll fire our own on stop request when we are done 1.510 + // processing the data.... 1.511 + 1.512 + return rv; 1.513 +}