modules/libjar/nsJARInputStream.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/modules/libjar/nsJARInputStream.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,370 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* nsJARInputStream.cpp
     1.6 + * 
     1.7 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    1.10 +
    1.11 +#include "nsJARInputStream.h"
    1.12 +#include "zipstruct.h"         // defines ZIP compression codes
    1.13 +#include "nsZipArchive.h"
    1.14 +
    1.15 +#include "nsNetUtil.h"
    1.16 +#include "nsEscape.h"
    1.17 +#include "nsIFile.h"
    1.18 +#include "nsDebug.h"
    1.19 +#include <algorithm>
    1.20 +#if defined(XP_WIN)
    1.21 +#include <windows.h>
    1.22 +#endif
    1.23 +
    1.24 +/*---------------------------------------------
    1.25 + *  nsISupports implementation
    1.26 + *--------------------------------------------*/
    1.27 +
    1.28 +NS_IMPL_ISUPPORTS(nsJARInputStream, nsIInputStream)
    1.29 +
    1.30 +/*----------------------------------------------------------
    1.31 + * nsJARInputStream implementation
    1.32 + *--------------------------------------------------------*/
    1.33 +
    1.34 +nsresult
    1.35 +nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item)
    1.36 +{
    1.37 +    nsresult rv = NS_OK;
    1.38 +    NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
    1.39 +    NS_ABORT_IF_FALSE(item, "Argument may not be null");
    1.40 +
    1.41 +    // Mark it as closed, in case something fails in initialisation
    1.42 +    mMode = MODE_CLOSED;
    1.43 +    //-- prepare for the compression type
    1.44 +    switch (item->Compression()) {
    1.45 +       case STORED: 
    1.46 +           mMode = MODE_COPY;
    1.47 +           break;
    1.48 +
    1.49 +       case DEFLATED:
    1.50 +           rv = gZlibInit(&mZs);
    1.51 +           NS_ENSURE_SUCCESS(rv, rv);
    1.52 +    
    1.53 +           mMode = MODE_INFLATE;
    1.54 +           mInCrc = item->CRC32();
    1.55 +           mOutCrc = crc32(0L, Z_NULL, 0);
    1.56 +           break;
    1.57 +
    1.58 +       default:
    1.59 +           return NS_ERROR_NOT_IMPLEMENTED;
    1.60 +    }
    1.61 +   
    1.62 +    // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
    1.63 +    mFd = aJar->mZip->GetFD();
    1.64 +    mZs.next_in = (Bytef *)aJar->mZip->GetData(item);
    1.65 +    if (!mZs.next_in)
    1.66 +        return NS_ERROR_FILE_CORRUPTED;
    1.67 +    mZs.avail_in = item->Size();
    1.68 +    mOutSize = item->RealSize();
    1.69 +    mZs.total_out = 0;
    1.70 +    return NS_OK;
    1.71 +}
    1.72 +
    1.73 +nsresult
    1.74 +nsJARInputStream::InitDirectory(nsJAR* aJar,
    1.75 +                                const nsACString& aJarDirSpec,
    1.76 +                                const char* aDir)
    1.77 +{
    1.78 +    NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
    1.79 +    NS_ABORT_IF_FALSE(aDir, "Argument may not be null");
    1.80 +
    1.81 +    // Mark it as closed, in case something fails in initialisation
    1.82 +    mMode = MODE_CLOSED;
    1.83 +    
    1.84 +    // Keep the zipReader for getting the actual zipItems
    1.85 +    mJar = aJar;
    1.86 +    nsZipFind *find;
    1.87 +    nsresult rv;
    1.88 +    // We can get aDir's contents as strings via FindEntries
    1.89 +    // with the following pattern (see nsIZipReader.findEntries docs)
    1.90 +    // assuming dirName is properly escaped:
    1.91 +    //
    1.92 +    //   dirName + "?*~" + dirName + "?*/?*"
    1.93 +    nsDependentCString dirName(aDir);
    1.94 +    mNameLen = dirName.Length();
    1.95 +
    1.96 +    // iterate through dirName and copy it to escDirName, escaping chars
    1.97 +    // which are special at the "top" level of the regexp so FindEntries
    1.98 +    // works correctly
    1.99 +    nsAutoCString escDirName;
   1.100 +    const char* curr = dirName.BeginReading();
   1.101 +    const char* end  = dirName.EndReading();
   1.102 +    while (curr != end) {
   1.103 +        switch (*curr) {
   1.104 +            case '*':
   1.105 +            case '?':
   1.106 +            case '$':
   1.107 +            case '[':
   1.108 +            case ']':
   1.109 +            case '^':
   1.110 +            case '~':
   1.111 +            case '(':
   1.112 +            case ')':
   1.113 +            case '\\':
   1.114 +                escDirName.Append('\\');
   1.115 +                // fall through
   1.116 +            default:
   1.117 +                escDirName.Append(*curr);
   1.118 +        }
   1.119 +        ++curr;
   1.120 +    }
   1.121 +    nsAutoCString pattern = escDirName + NS_LITERAL_CSTRING("?*~") +
   1.122 +                            escDirName + NS_LITERAL_CSTRING("?*/?*");
   1.123 +    rv = mJar->mZip->FindInit(pattern.get(), &find);
   1.124 +    if (NS_FAILED(rv)) return rv;
   1.125 +
   1.126 +    const char *name;
   1.127 +    uint16_t nameLen;
   1.128 +    while ((rv = find->FindNext( &name, &nameLen )) == NS_OK) {
   1.129 +        // Must copy, to make it zero-terminated
   1.130 +        mArray.AppendElement(nsCString(name,nameLen));
   1.131 +    }
   1.132 +    delete find;
   1.133 +
   1.134 +    if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && NS_FAILED(rv)) {
   1.135 +        return NS_ERROR_FAILURE;    // no error translation
   1.136 +    }
   1.137 +
   1.138 +    // Sort it
   1.139 +    mArray.Sort();
   1.140 +
   1.141 +    mBuffer.AssignLiteral("300: ");
   1.142 +    mBuffer.Append(aJarDirSpec);
   1.143 +    mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n");
   1.144 +
   1.145 +    // Open for reading
   1.146 +    mMode = MODE_DIRECTORY;
   1.147 +    mZs.total_out = 0;
   1.148 +    mArrPos = 0;
   1.149 +    return NS_OK;
   1.150 +}
   1.151 +
   1.152 +NS_IMETHODIMP 
   1.153 +nsJARInputStream::Available(uint64_t *_retval)
   1.154 +{
   1.155 +    // A lot of callers don't check the error code.
   1.156 +    // They just use the _retval value.
   1.157 +    *_retval = 0;
   1.158 +
   1.159 +    switch (mMode) {
   1.160 +      case MODE_NOTINITED:
   1.161 +        break;
   1.162 +
   1.163 +      case MODE_CLOSED:
   1.164 +        return NS_BASE_STREAM_CLOSED;
   1.165 +
   1.166 +      case MODE_DIRECTORY:
   1.167 +        *_retval = mBuffer.Length();
   1.168 +        break;
   1.169 +
   1.170 +      case MODE_INFLATE:
   1.171 +      case MODE_COPY:
   1.172 +        *_retval = mOutSize - mZs.total_out;
   1.173 +        break;
   1.174 +    }
   1.175 +
   1.176 +    return NS_OK;
   1.177 +}
   1.178 +
   1.179 +NS_IMETHODIMP
   1.180 +nsJARInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead)
   1.181 +{
   1.182 +    NS_ENSURE_ARG_POINTER(aBuffer);
   1.183 +    NS_ENSURE_ARG_POINTER(aBytesRead);
   1.184 +
   1.185 +    *aBytesRead = 0;
   1.186 +
   1.187 +    nsresult rv = NS_OK;
   1.188 +MOZ_WIN_MEM_TRY_BEGIN
   1.189 +    switch (mMode) {
   1.190 +      case MODE_NOTINITED:
   1.191 +        return NS_OK;
   1.192 +
   1.193 +      case MODE_CLOSED:
   1.194 +        return NS_BASE_STREAM_CLOSED;
   1.195 +
   1.196 +      case MODE_DIRECTORY:
   1.197 +        return ReadDirectory(aBuffer, aCount, aBytesRead);
   1.198 +
   1.199 +      case MODE_INFLATE:
   1.200 +        if (mFd) {
   1.201 +          rv = ContinueInflate(aBuffer, aCount, aBytesRead);
   1.202 +        }
   1.203 +        // be aggressive about releasing the file!
   1.204 +        // note that sometimes, we will release  mFd before we've finished
   1.205 +        // deflating - this is because zlib buffers the input
   1.206 +        if (mZs.avail_in == 0) {
   1.207 +            mFd = nullptr;
   1.208 +        }
   1.209 +        break;
   1.210 +
   1.211 +      case MODE_COPY:
   1.212 +        if (mFd) {
   1.213 +          uint32_t count = std::min(aCount, mOutSize - uint32_t(mZs.total_out));
   1.214 +          if (count) {
   1.215 +              memcpy(aBuffer, mZs.next_in + mZs.total_out, count);
   1.216 +              mZs.total_out += count;
   1.217 +          }
   1.218 +          *aBytesRead = count;
   1.219 +        }
   1.220 +        // be aggressive about releasing the file!
   1.221 +        // note that sometimes, we will release mFd before we've finished copying.
   1.222 +        if (mZs.total_out >= mOutSize) {
   1.223 +            mFd = nullptr;
   1.224 +        }
   1.225 +        break;
   1.226 +    }
   1.227 +MOZ_WIN_MEM_TRY_CATCH(rv = NS_ERROR_FAILURE)
   1.228 +    return rv;
   1.229 +}
   1.230 +
   1.231 +NS_IMETHODIMP
   1.232 +nsJARInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval)
   1.233 +{
   1.234 +    // don't have a buffer to read from, so this better not be called!
   1.235 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.236 +}
   1.237 +
   1.238 +NS_IMETHODIMP
   1.239 +nsJARInputStream::IsNonBlocking(bool *aNonBlocking)
   1.240 +{
   1.241 +    *aNonBlocking = false;
   1.242 +    return NS_OK;
   1.243 +}
   1.244 +
   1.245 +NS_IMETHODIMP
   1.246 +nsJARInputStream::Close()
   1.247 +{
   1.248 +    if (mMode == MODE_INFLATE) {
   1.249 +        inflateEnd(&mZs);
   1.250 +    }
   1.251 +    mMode = MODE_CLOSED;
   1.252 +    mFd = nullptr;
   1.253 +    return NS_OK;
   1.254 +}
   1.255 +
   1.256 +nsresult 
   1.257 +nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount,
   1.258 +                                  uint32_t* aBytesRead)
   1.259 +{
   1.260 +    // No need to check the args, ::Read did that, but assert them at least
   1.261 +    NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
   1.262 +    NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
   1.263 +
   1.264 +    // Keep old total_out count
   1.265 +    const uint32_t oldTotalOut = mZs.total_out;
   1.266 +    
   1.267 +    // make sure we aren't reading too much
   1.268 +    mZs.avail_out = std::min(aCount, (mOutSize-oldTotalOut));
   1.269 +    mZs.next_out = (unsigned char*)aBuffer;
   1.270 +
   1.271 +    // now inflate
   1.272 +    int zerr = inflate(&mZs, Z_SYNC_FLUSH);
   1.273 +    if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
   1.274 +        return NS_ERROR_FILE_CORRUPTED;
   1.275 +
   1.276 +    *aBytesRead = (mZs.total_out - oldTotalOut);
   1.277 +
   1.278 +    // Calculate the CRC on the output
   1.279 +    mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
   1.280 +
   1.281 +    // be aggressive about ending the inflation
   1.282 +    // for some reason we don't always get Z_STREAM_END
   1.283 +    if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
   1.284 +        inflateEnd(&mZs);
   1.285 +
   1.286 +        // stop returning valid data as soon as we know we have a bad CRC
   1.287 +        if (mOutCrc != mInCrc)
   1.288 +            return NS_ERROR_FILE_CORRUPTED;
   1.289 +    }
   1.290 +
   1.291 +    return NS_OK;
   1.292 +}
   1.293 +
   1.294 +nsresult
   1.295 +nsJARInputStream::ReadDirectory(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead)
   1.296 +{
   1.297 +    // No need to check the args, ::Read did that, but assert them at least
   1.298 +    NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
   1.299 +    NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
   1.300 +
   1.301 +    // If the buffer contains data, copy what's there up to the desired amount
   1.302 +    uint32_t numRead = CopyDataToBuffer(aBuffer, aCount);
   1.303 +
   1.304 +    if (aCount > 0) {
   1.305 +        // empty the buffer and start writing directory entry lines to it
   1.306 +        mBuffer.Truncate();
   1.307 +        mCurPos = 0;
   1.308 +        const uint32_t arrayLen = mArray.Length();
   1.309 +
   1.310 +        for ( ;aCount > mBuffer.Length(); mArrPos++) {
   1.311 +            // have we consumed all the directory contents?
   1.312 +            if (arrayLen <= mArrPos)
   1.313 +                break;
   1.314 +
   1.315 +            const char * entryName = mArray[mArrPos].get();
   1.316 +            uint32_t entryNameLen = mArray[mArrPos].Length();
   1.317 +            nsZipItem* ze = mJar->mZip->GetItem(entryName);
   1.318 +            NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
   1.319 +
   1.320 +            // Last Modified Time
   1.321 +            PRExplodedTime tm;
   1.322 +            PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm);
   1.323 +            char itemLastModTime[65];
   1.324 +            PR_FormatTimeUSEnglish(itemLastModTime,
   1.325 +                                   sizeof(itemLastModTime),
   1.326 +                                   " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ",
   1.327 +                                   &tm);
   1.328 +
   1.329 +            // write a 201: line to the buffer for this item
   1.330 +            // 200: filename content-length last-modified file-type
   1.331 +            mBuffer.AppendLiteral("201: ");
   1.332 +
   1.333 +            // Names must be escaped and relative, so use the pre-calculated length
   1.334 +            // of the directory name as the offset into the string
   1.335 +            // NS_EscapeURL adds the escaped URL to the give string buffer
   1.336 +            NS_EscapeURL(entryName + mNameLen,
   1.337 +                         entryNameLen - mNameLen, 
   1.338 +                         esc_Minimal | esc_AlwaysCopy,
   1.339 +                         mBuffer);
   1.340 +
   1.341 +            mBuffer.Append(' ');
   1.342 +            mBuffer.AppendInt(ze->RealSize(), 10);
   1.343 +            mBuffer.Append(itemLastModTime); // starts/ends with ' '
   1.344 +            if (ze->IsDirectory()) 
   1.345 +                mBuffer.AppendLiteral("DIRECTORY\n");
   1.346 +            else
   1.347 +                mBuffer.AppendLiteral("FILE\n");
   1.348 +        }
   1.349 +
   1.350 +        // Copy up to the desired amount of data to buffer
   1.351 +        numRead += CopyDataToBuffer(aBuffer, aCount);
   1.352 +    }
   1.353 +
   1.354 +    *aBytesRead = numRead;
   1.355 +    return NS_OK;
   1.356 +}
   1.357 +
   1.358 +uint32_t
   1.359 +nsJARInputStream::CopyDataToBuffer(char* &aBuffer, uint32_t &aCount)
   1.360 +{
   1.361 +    const uint32_t writeLength = std::min(aCount, mBuffer.Length() - mCurPos);
   1.362 +
   1.363 +    if (writeLength > 0) {
   1.364 +        memcpy(aBuffer, mBuffer.get() + mCurPos, writeLength);
   1.365 +        mCurPos += writeLength;
   1.366 +        aCount  -= writeLength;
   1.367 +        aBuffer += writeLength;
   1.368 +    }
   1.369 +
   1.370 +    // return number of bytes copied to the buffer so the
   1.371 +    // Read method can return the number of bytes copied
   1.372 +    return writeLength;
   1.373 +}

mercurial