modules/libjar/zipwriter/src/nsZipHeader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/modules/libjar/zipwriter/src/nsZipHeader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,395 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 + */
     1.8 +
     1.9 +#include "StreamFunctions.h"
    1.10 +#include "nsZipHeader.h"
    1.11 +#include "nsMemory.h"
    1.12 +#include "prtime.h"
    1.13 +
    1.14 +#define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
    1.15 +#define ZIP_FILE_HEADER_SIZE 30
    1.16 +#define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
    1.17 +#define ZIP_CDS_HEADER_SIZE 46
    1.18 +
    1.19 +#define FLAGS_IS_UTF8 0x800
    1.20 +
    1.21 +#define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
    1.22 +#define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
    1.23 +
    1.24 +/**
    1.25 + * nsZipHeader represents an entry from a zip file.
    1.26 + */
    1.27 +NS_IMPL_ISUPPORTS(nsZipHeader, nsIZipEntry)
    1.28 +
    1.29 +/* readonly attribute unsigned short compression; */
    1.30 +NS_IMETHODIMP nsZipHeader::GetCompression(uint16_t *aCompression)
    1.31 +{
    1.32 +    NS_ASSERTION(mInited, "Not initalised");
    1.33 +
    1.34 +    *aCompression = mMethod;
    1.35 +    return NS_OK;
    1.36 +}
    1.37 +
    1.38 +/* readonly attribute unsigned long size; */
    1.39 +NS_IMETHODIMP nsZipHeader::GetSize(uint32_t *aSize)
    1.40 +{
    1.41 +    NS_ASSERTION(mInited, "Not initalised");
    1.42 +
    1.43 +    *aSize = mCSize;
    1.44 +    return NS_OK;
    1.45 +}
    1.46 +
    1.47 +/* readonly attribute unsigned long realSize; */
    1.48 +NS_IMETHODIMP nsZipHeader::GetRealSize(uint32_t *aRealSize)
    1.49 +{
    1.50 +    NS_ASSERTION(mInited, "Not initalised");
    1.51 +
    1.52 +    *aRealSize = mUSize;
    1.53 +    return NS_OK;
    1.54 +}
    1.55 +
    1.56 +/* readonly attribute unsigned long CRC32; */
    1.57 +NS_IMETHODIMP nsZipHeader::GetCRC32(uint32_t *aCRC32)
    1.58 +{
    1.59 +    NS_ASSERTION(mInited, "Not initalised");
    1.60 +
    1.61 +    *aCRC32 = mCRC;
    1.62 +    return NS_OK;
    1.63 +}
    1.64 +
    1.65 +/* readonly attribute boolean isDirectory; */
    1.66 +NS_IMETHODIMP nsZipHeader::GetIsDirectory(bool *aIsDirectory)
    1.67 +{
    1.68 +    NS_ASSERTION(mInited, "Not initalised");
    1.69 +
    1.70 +    if (mName.Last() == '/')
    1.71 +        *aIsDirectory = true;
    1.72 +    else
    1.73 +        *aIsDirectory = false;
    1.74 +    return NS_OK;
    1.75 +}
    1.76 +
    1.77 +/* readonly attribute PRTime lastModifiedTime; */
    1.78 +NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime)
    1.79 +{
    1.80 +    NS_ASSERTION(mInited, "Not initalised");
    1.81 +
    1.82 +    // Try to read timestamp from extra field
    1.83 +    uint16_t blocksize;
    1.84 +    const uint8_t *tsField = GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, false, &blocksize);
    1.85 +    if (tsField && blocksize >= 5) {
    1.86 +        uint32_t pos = 4;
    1.87 +        uint8_t flags;
    1.88 +        flags = READ8(tsField, &pos);
    1.89 +        if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) {
    1.90 +            *aLastModifiedTime = (PRTime)(READ32(tsField, &pos))
    1.91 +                                 * PR_USEC_PER_SEC;
    1.92 +            return NS_OK;
    1.93 +        }
    1.94 +    }
    1.95 +
    1.96 +    // Use DOS date/time fields
    1.97 +    // Note that on DST shift we can't handle correctly the hour that is valid
    1.98 +    // in both DST zones
    1.99 +    PRExplodedTime time;
   1.100 +
   1.101 +    time.tm_usec = 0;
   1.102 +
   1.103 +    time.tm_hour = (mTime >> 11) & 0x1F;
   1.104 +    time.tm_min = (mTime >> 5) & 0x3F;
   1.105 +    time.tm_sec = (mTime & 0x1F) * 2;
   1.106 +
   1.107 +    time.tm_year = (mDate >> 9) + 1980;
   1.108 +    time.tm_month = ((mDate >> 5) & 0x0F) - 1;
   1.109 +    time.tm_mday = mDate & 0x1F;
   1.110 +
   1.111 +    time.tm_params.tp_gmt_offset = 0;
   1.112 +    time.tm_params.tp_dst_offset = 0;
   1.113 +
   1.114 +    PR_NormalizeTime(&time, PR_GMTParameters);
   1.115 +    time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
   1.116 +    PR_NormalizeTime(&time, PR_GMTParameters);
   1.117 +    time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
   1.118 +
   1.119 +    *aLastModifiedTime = PR_ImplodeTime(&time);
   1.120 +
   1.121 +    return NS_OK;
   1.122 +}
   1.123 +
   1.124 +/* readonly attribute boolean isSynthetic; */
   1.125 +NS_IMETHODIMP nsZipHeader::GetIsSynthetic(bool *aIsSynthetic)
   1.126 +{
   1.127 +    NS_ASSERTION(mInited, "Not initalised");
   1.128 +
   1.129 +    *aIsSynthetic = false;
   1.130 +    return NS_OK;
   1.131 +}
   1.132 +
   1.133 +/* readonly attribute unsigned long permissions; */
   1.134 +NS_IMETHODIMP nsZipHeader::GetPermissions(uint32_t *aPermissions)
   1.135 +{
   1.136 +    NS_ASSERTION(mInited, "Not initalised");
   1.137 +
   1.138 +    // Always give user read access at least, this matches nsIZipReader's behaviour
   1.139 +    *aPermissions = ((mEAttr >> 16) & 0xfff) | 0x100;
   1.140 +    return NS_OK;
   1.141 +}
   1.142 +
   1.143 +void nsZipHeader::Init(const nsACString & aPath, PRTime aDate, uint32_t aAttr,
   1.144 +                       uint32_t aOffset)
   1.145 +{
   1.146 +    NS_ASSERTION(!mInited, "Already initalised");
   1.147 +
   1.148 +    PRExplodedTime time;
   1.149 +    PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
   1.150 +
   1.151 +    mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
   1.152 +    mDate = time.tm_mday + ((time.tm_month + 1) << 5) +
   1.153 +            ((time.tm_year - 1980) << 9);
   1.154 +
   1.155 +    // Store modification timestamp as extra field
   1.156 +    // First fill CDS extra field
   1.157 +    mFieldLength = 9;
   1.158 +    mExtraField = new uint8_t[mFieldLength];
   1.159 +    if (!mExtraField) {
   1.160 +        mFieldLength = 0;
   1.161 +    } else {
   1.162 +        uint32_t pos = 0;
   1.163 +        WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD);
   1.164 +        WRITE16(mExtraField.get(), &pos, 5);
   1.165 +        WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME);
   1.166 +        WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC);
   1.167 +
   1.168 +        // Fill local extra field
   1.169 +        mLocalExtraField = new uint8_t[mFieldLength];
   1.170 +        if (mLocalExtraField) {
   1.171 +            mLocalFieldLength = mFieldLength;
   1.172 +            memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
   1.173 +        }
   1.174 +    }
   1.175 +
   1.176 +    mEAttr = aAttr;
   1.177 +    mOffset = aOffset;
   1.178 +    mName = aPath;
   1.179 +    mComment = NS_LITERAL_CSTRING("");
   1.180 +    // Claim a UTF-8 path in case it needs it.
   1.181 +    mFlags |= FLAGS_IS_UTF8;
   1.182 +    mInited = true;
   1.183 +}
   1.184 +
   1.185 +uint32_t nsZipHeader::GetFileHeaderLength()
   1.186 +{
   1.187 +    return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
   1.188 +}
   1.189 +
   1.190 +nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream)
   1.191 +{
   1.192 +    NS_ASSERTION(mInited, "Not initalised");
   1.193 +
   1.194 +    uint8_t buf[ZIP_FILE_HEADER_SIZE];
   1.195 +    uint32_t pos = 0;
   1.196 +    WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
   1.197 +    WRITE16(buf, &pos, mVersionNeeded);
   1.198 +    WRITE16(buf, &pos, mFlags);
   1.199 +    WRITE16(buf, &pos, mMethod);
   1.200 +    WRITE16(buf, &pos, mTime);
   1.201 +    WRITE16(buf, &pos, mDate);
   1.202 +    WRITE32(buf, &pos, mCRC);
   1.203 +    WRITE32(buf, &pos, mCSize);
   1.204 +    WRITE32(buf, &pos, mUSize);
   1.205 +    WRITE16(buf, &pos, mName.Length());
   1.206 +    WRITE16(buf, &pos, mLocalFieldLength);
   1.207 +
   1.208 +    nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
   1.209 +    NS_ENSURE_SUCCESS(rv, rv);
   1.210 +
   1.211 +    rv = ZW_WriteData(aStream, mName.get(), mName.Length());
   1.212 +    NS_ENSURE_SUCCESS(rv, rv);
   1.213 +
   1.214 +    if (mLocalFieldLength)
   1.215 +    {
   1.216 +      rv = ZW_WriteData(aStream, (const char *)mLocalExtraField.get(), mLocalFieldLength);
   1.217 +      NS_ENSURE_SUCCESS(rv, rv);
   1.218 +    }
   1.219 +
   1.220 +    return NS_OK;
   1.221 +}
   1.222 +
   1.223 +uint32_t nsZipHeader::GetCDSHeaderLength()
   1.224 +{
   1.225 +    return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
   1.226 +           mFieldLength;
   1.227 +}
   1.228 +
   1.229 +nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream)
   1.230 +{
   1.231 +    NS_ASSERTION(mInited, "Not initalised");
   1.232 +
   1.233 +    uint8_t buf[ZIP_CDS_HEADER_SIZE];
   1.234 +    uint32_t pos = 0;
   1.235 +    WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
   1.236 +    WRITE16(buf, &pos, mVersionMade);
   1.237 +    WRITE16(buf, &pos, mVersionNeeded);
   1.238 +    WRITE16(buf, &pos, mFlags);
   1.239 +    WRITE16(buf, &pos, mMethod);
   1.240 +    WRITE16(buf, &pos, mTime);
   1.241 +    WRITE16(buf, &pos, mDate);
   1.242 +    WRITE32(buf, &pos, mCRC);
   1.243 +    WRITE32(buf, &pos, mCSize);
   1.244 +    WRITE32(buf, &pos, mUSize);
   1.245 +    WRITE16(buf, &pos, mName.Length());
   1.246 +    WRITE16(buf, &pos, mFieldLength);
   1.247 +    WRITE16(buf, &pos, mComment.Length());
   1.248 +    WRITE16(buf, &pos, mDisk);
   1.249 +    WRITE16(buf, &pos, mIAttr);
   1.250 +    WRITE32(buf, &pos, mEAttr);
   1.251 +    WRITE32(buf, &pos, mOffset);
   1.252 +
   1.253 +    nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
   1.254 +    NS_ENSURE_SUCCESS(rv, rv);
   1.255 +
   1.256 +    rv = ZW_WriteData(aStream, mName.get(), mName.Length());
   1.257 +    NS_ENSURE_SUCCESS(rv, rv);
   1.258 +    if (mExtraField) {
   1.259 +        rv = ZW_WriteData(aStream, (const char *)mExtraField.get(), mFieldLength);
   1.260 +        NS_ENSURE_SUCCESS(rv, rv);
   1.261 +    }
   1.262 +    return ZW_WriteData(aStream, mComment.get(), mComment.Length());
   1.263 +}
   1.264 +
   1.265 +nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream)
   1.266 +{
   1.267 +    NS_ASSERTION(!mInited, "Already initalised");
   1.268 +
   1.269 +    uint8_t buf[ZIP_CDS_HEADER_SIZE];
   1.270 +
   1.271 +    nsresult rv = ZW_ReadData(stream, (char *)buf, ZIP_CDS_HEADER_SIZE);
   1.272 +    NS_ENSURE_SUCCESS(rv, rv);
   1.273 +
   1.274 +    uint32_t pos = 0;
   1.275 +    uint32_t signature = READ32(buf, &pos);
   1.276 +    if (signature != ZIP_CDS_HEADER_SIGNATURE)
   1.277 +        return NS_ERROR_FILE_CORRUPTED;
   1.278 +
   1.279 +    mVersionMade = READ16(buf, &pos);
   1.280 +    mVersionNeeded = READ16(buf, &pos);
   1.281 +    mFlags = READ16(buf, &pos);
   1.282 +    mMethod = READ16(buf, &pos);
   1.283 +    mTime = READ16(buf, &pos);
   1.284 +    mDate = READ16(buf, &pos);
   1.285 +    mCRC = READ32(buf, &pos);
   1.286 +    mCSize = READ32(buf, &pos);
   1.287 +    mUSize = READ32(buf, &pos);
   1.288 +    uint16_t namelength = READ16(buf, &pos);
   1.289 +    mFieldLength = READ16(buf, &pos);
   1.290 +    uint16_t commentlength = READ16(buf, &pos);
   1.291 +    mDisk = READ16(buf, &pos);
   1.292 +    mIAttr = READ16(buf, &pos);
   1.293 +    mEAttr = READ32(buf, &pos);
   1.294 +    mOffset = READ32(buf, &pos);
   1.295 +
   1.296 +    if (namelength > 0) {
   1.297 +        nsAutoArrayPtr<char> field(new char[namelength]);
   1.298 +        NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
   1.299 +        rv = ZW_ReadData(stream, field.get(), namelength);
   1.300 +        NS_ENSURE_SUCCESS(rv, rv);
   1.301 +        mName.Assign(field, namelength);
   1.302 +    }
   1.303 +    else
   1.304 +        mName = NS_LITERAL_CSTRING("");
   1.305 +
   1.306 +    if (mFieldLength > 0) {
   1.307 +        mExtraField = new uint8_t[mFieldLength];
   1.308 +        NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
   1.309 +        rv = ZW_ReadData(stream, (char *)mExtraField.get(), mFieldLength);
   1.310 +        NS_ENSURE_SUCCESS(rv, rv);
   1.311 +    }
   1.312 +
   1.313 +    if (commentlength > 0) {
   1.314 +        nsAutoArrayPtr<char> field(new char[commentlength]);
   1.315 +        NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
   1.316 +        rv = ZW_ReadData(stream, field.get(), commentlength);
   1.317 +        NS_ENSURE_SUCCESS(rv, rv);
   1.318 +        mComment.Assign(field, commentlength);
   1.319 +    }
   1.320 +    else
   1.321 +        mComment = NS_LITERAL_CSTRING("");
   1.322 +
   1.323 +    mInited = true;
   1.324 +    return NS_OK;
   1.325 +}
   1.326 +
   1.327 +const uint8_t * nsZipHeader::GetExtraField(uint16_t aTag, bool aLocal, uint16_t *aBlockSize)
   1.328 +{
   1.329 +    const uint8_t *buf = aLocal ? mLocalExtraField : mExtraField;
   1.330 +    uint32_t buflen = aLocal ? mLocalFieldLength : mFieldLength;
   1.331 +    uint32_t pos = 0;
   1.332 +    uint16_t tag, blocksize;
   1.333 +
   1.334 +    while (buf && (pos + 4) <= buflen) {
   1.335 +      tag = READ16(buf, &pos);
   1.336 +      blocksize = READ16(buf, &pos);
   1.337 +
   1.338 +      if (aTag == tag && (pos + blocksize) <= buflen) {
   1.339 +        *aBlockSize = blocksize;
   1.340 +        return buf + pos - 4;
   1.341 +      }
   1.342 +
   1.343 +      pos += blocksize;
   1.344 +    }
   1.345 +
   1.346 +    return nullptr;
   1.347 +}
   1.348 +
   1.349 +/*
   1.350 + * Pad extra field to align data starting position to specified size.
   1.351 + */
   1.352 +nsresult nsZipHeader::PadExtraField(uint32_t aOffset, uint16_t aAlignSize)
   1.353 +{
   1.354 +    uint32_t pad_size;
   1.355 +    uint32_t pa_offset;
   1.356 +    uint32_t pa_end;
   1.357 +
   1.358 +    // Check for range and power of 2.
   1.359 +    if (aAlignSize < 2 || aAlignSize > 32768 ||
   1.360 +        (aAlignSize & (aAlignSize - 1)) != 0) {
   1.361 +      return NS_ERROR_INVALID_ARG;
   1.362 +    }
   1.363 +
   1.364 +    // Point to current starting data position.
   1.365 +    aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
   1.366 +
   1.367 +    // Calculate aligned offset.
   1.368 +    pa_offset = aOffset & ~(aAlignSize - 1);
   1.369 +    pa_end = pa_offset + aAlignSize;
   1.370 +    pad_size = pa_end - aOffset;
   1.371 +    if (pad_size == 0) {
   1.372 +      return NS_OK;
   1.373 +    }
   1.374 +
   1.375 +    // Leave enough room(at least 4 bytes) for valid values in extra field.
   1.376 +    while (pad_size < 4) {
   1.377 +      pad_size += aAlignSize;
   1.378 +    }
   1.379 +    // Extra field length is 2 bytes.
   1.380 +    if (mLocalFieldLength + pad_size > 65535) {
   1.381 +      return NS_ERROR_FAILURE;
   1.382 +    }
   1.383 +
   1.384 +    nsAutoArrayPtr<uint8_t> field = mLocalExtraField;
   1.385 +    uint32_t pos = mLocalFieldLength;
   1.386 +
   1.387 +    mLocalExtraField = new uint8_t[mLocalFieldLength + pad_size];
   1.388 +    memcpy(mLocalExtraField.get(), field, mLocalFieldLength);
   1.389 +    // Use 0xFFFF as tag ID to avoid conflict with other IDs.
   1.390 +    // For more information, please read "Extensible data fields" section in:
   1.391 +    // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
   1.392 +    WRITE16(mLocalExtraField.get(), &pos, 0xFFFF);
   1.393 +    WRITE16(mLocalExtraField.get(), &pos, pad_size - 4);
   1.394 +    memset(mLocalExtraField.get() + pos, 0, pad_size - 4);
   1.395 +    mLocalFieldLength += pad_size;
   1.396 +
   1.397 +    return NS_OK;
   1.398 +}

mercurial