tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsZipHeader.cpp (10877B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 */
      5 
      6 #include "StreamFunctions.h"
      7 #include "nsZipHeader.h"
      8 #include "prtime.h"
      9 
     10 #define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
     11 #define ZIP_FILE_HEADER_SIZE 30
     12 #define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
     13 #define ZIP_CDS_HEADER_SIZE 46
     14 
     15 #define FLAGS_IS_UTF8 0x800
     16 
     17 #define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
     18 #define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
     19 
     20 using namespace mozilla;
     21 
     22 /**
     23 * nsZipHeader represents an entry from a zip file.
     24 */
     25 NS_IMPL_ISUPPORTS(nsZipHeader, nsIZipEntry)
     26 
     27 NS_IMETHODIMP nsZipHeader::GetCompression(uint16_t* aCompression) {
     28  NS_ASSERTION(mInited, "Not initalised");
     29 
     30  *aCompression = mMethod;
     31  return NS_OK;
     32 }
     33 
     34 NS_IMETHODIMP nsZipHeader::GetSize(uint32_t* aSize) {
     35  NS_ASSERTION(mInited, "Not initalised");
     36 
     37  *aSize = mCSize;
     38  return NS_OK;
     39 }
     40 
     41 NS_IMETHODIMP nsZipHeader::GetRealSize(uint32_t* aRealSize) {
     42  NS_ASSERTION(mInited, "Not initalised");
     43 
     44  *aRealSize = mUSize;
     45  return NS_OK;
     46 }
     47 
     48 NS_IMETHODIMP nsZipHeader::GetCRC32(uint32_t* aCRC32) {
     49  NS_ASSERTION(mInited, "Not initalised");
     50 
     51  *aCRC32 = mCRC;
     52  return NS_OK;
     53 }
     54 
     55 NS_IMETHODIMP nsZipHeader::GetIsDirectory(bool* aIsDirectory) {
     56  NS_ASSERTION(mInited, "Not initalised");
     57 
     58  if (mName.Last() == '/')
     59    *aIsDirectory = true;
     60  else
     61    *aIsDirectory = false;
     62  return NS_OK;
     63 }
     64 
     65 NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime* aLastModifiedTime) {
     66  NS_ASSERTION(mInited, "Not initalised");
     67 
     68  // Try to read timestamp from extra field
     69  uint16_t blocksize;
     70  const uint8_t* tsField =
     71      GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, false, &blocksize);
     72  if (tsField && blocksize >= 5) {
     73    uint32_t pos = 4;
     74    uint8_t flags;
     75    flags = READ8(tsField, &pos);
     76    if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) {
     77      *aLastModifiedTime = (PRTime)(READ32(tsField, &pos))*PR_USEC_PER_SEC;
     78      return NS_OK;
     79    }
     80  }
     81 
     82  // Use DOS date/time fields
     83  // Note that on DST shift we can't handle correctly the hour that is valid
     84  // in both DST zones
     85  PRExplodedTime time;
     86 
     87  time.tm_usec = 0;
     88 
     89  time.tm_hour = (mTime >> 11) & 0x1F;
     90  time.tm_min = (mTime >> 5) & 0x3F;
     91  time.tm_sec = (mTime & 0x1F) * 2;
     92 
     93  time.tm_year = (mDate >> 9) + 1980;
     94  time.tm_month = ((mDate >> 5) & 0x0F) - 1;
     95  time.tm_mday = mDate & 0x1F;
     96 
     97  time.tm_params.tp_gmt_offset = 0;
     98  time.tm_params.tp_dst_offset = 0;
     99 
    100  PR_NormalizeTime(&time, PR_GMTParameters);
    101  time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
    102  PR_NormalizeTime(&time, PR_GMTParameters);
    103  time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
    104 
    105  *aLastModifiedTime = PR_ImplodeTime(&time);
    106 
    107  return NS_OK;
    108 }
    109 
    110 NS_IMETHODIMP nsZipHeader::GetIsSynthetic(bool* aIsSynthetic) {
    111  NS_ASSERTION(mInited, "Not initalised");
    112 
    113  *aIsSynthetic = false;
    114  return NS_OK;
    115 }
    116 
    117 NS_IMETHODIMP nsZipHeader::GetPermissions(uint32_t* aPermissions) {
    118  NS_ASSERTION(mInited, "Not initalised");
    119 
    120  // Always give user read access at least, this matches nsIZipReader's
    121  // behaviour
    122  *aPermissions = ((mEAttr >> 16) & 0xfff) | 0x100;
    123  return NS_OK;
    124 }
    125 
    126 nsresult nsZipHeader::Init(const nsACString& aPath, PRTime aDate,
    127                           uint32_t aAttr, uint32_t aOffset) {
    128  NS_ASSERTION(!mInited, "Already initalised");
    129 
    130  PRExplodedTime time;
    131  PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
    132  if (time.tm_year < 1980) {
    133    return NS_ERROR_INVALID_ARG;
    134  }
    135 
    136  mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
    137  mDate =
    138      time.tm_mday + ((time.tm_month + 1) << 5) + ((time.tm_year - 1980) << 9);
    139 
    140  // Store modification timestamp as extra field
    141  // First fill CDS extra field
    142  mFieldLength = 9;
    143  mExtraField = MakeUnique<uint8_t[]>(mFieldLength);
    144  if (!mExtraField) {
    145    mFieldLength = 0;
    146  } else {
    147    uint32_t pos = 0;
    148    WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD);
    149    WRITE16(mExtraField.get(), &pos, 5);
    150    WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME);
    151    WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC);
    152 
    153    // Fill local extra field
    154    mLocalExtraField = MakeUnique<uint8_t[]>(mFieldLength);
    155    if (mLocalExtraField) {
    156      mLocalFieldLength = mFieldLength;
    157      memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
    158    }
    159  }
    160 
    161  mEAttr = aAttr;
    162  mOffset = aOffset;
    163  mName = aPath;
    164  mComment.Truncate();
    165  // Claim a UTF-8 path in case it needs it.
    166  mFlags |= FLAGS_IS_UTF8;
    167  mInited = true;
    168 
    169  return NS_OK;
    170 }
    171 
    172 uint32_t nsZipHeader::GetFileHeaderLength() {
    173  return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
    174 }
    175 
    176 nsresult nsZipHeader::WriteFileHeader(nsIOutputStream* aStream) {
    177  NS_ASSERTION(mInited, "Not initalised");
    178 
    179  uint8_t buf[ZIP_FILE_HEADER_SIZE];
    180  uint32_t pos = 0;
    181  WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
    182  WRITE16(buf, &pos, mVersionNeeded);
    183  WRITE16(buf, &pos, mFlags);
    184  WRITE16(buf, &pos, mMethod);
    185  WRITE16(buf, &pos, mTime);
    186  WRITE16(buf, &pos, mDate);
    187  WRITE32(buf, &pos, mCRC);
    188  WRITE32(buf, &pos, mCSize);
    189  WRITE32(buf, &pos, mUSize);
    190  WRITE16(buf, &pos, mName.Length());
    191  WRITE16(buf, &pos, mLocalFieldLength);
    192 
    193  nsresult rv = ZW_WriteData(aStream, (const char*)buf, pos);
    194  NS_ENSURE_SUCCESS(rv, rv);
    195 
    196  rv = ZW_WriteData(aStream, mName.get(), mName.Length());
    197  NS_ENSURE_SUCCESS(rv, rv);
    198 
    199  if (mLocalFieldLength) {
    200    rv = ZW_WriteData(aStream, (const char*)mLocalExtraField.get(),
    201                      mLocalFieldLength);
    202    NS_ENSURE_SUCCESS(rv, rv);
    203  }
    204 
    205  return NS_OK;
    206 }
    207 
    208 uint32_t nsZipHeader::GetCDSHeaderLength() {
    209  return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
    210         mFieldLength;
    211 }
    212 
    213 nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream* aStream) {
    214  NS_ASSERTION(mInited, "Not initalised");
    215 
    216  uint8_t buf[ZIP_CDS_HEADER_SIZE];
    217  uint32_t pos = 0;
    218  WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
    219  WRITE16(buf, &pos, mVersionMade);
    220  WRITE16(buf, &pos, mVersionNeeded);
    221  WRITE16(buf, &pos, mFlags);
    222  WRITE16(buf, &pos, mMethod);
    223  WRITE16(buf, &pos, mTime);
    224  WRITE16(buf, &pos, mDate);
    225  WRITE32(buf, &pos, mCRC);
    226  WRITE32(buf, &pos, mCSize);
    227  WRITE32(buf, &pos, mUSize);
    228  WRITE16(buf, &pos, mName.Length());
    229  WRITE16(buf, &pos, mFieldLength);
    230  WRITE16(buf, &pos, mComment.Length());
    231  WRITE16(buf, &pos, mDisk);
    232  WRITE16(buf, &pos, mIAttr);
    233  WRITE32(buf, &pos, mEAttr);
    234  WRITE32(buf, &pos, mOffset);
    235 
    236  nsresult rv = ZW_WriteData(aStream, (const char*)buf, pos);
    237  NS_ENSURE_SUCCESS(rv, rv);
    238 
    239  rv = ZW_WriteData(aStream, mName.get(), mName.Length());
    240  NS_ENSURE_SUCCESS(rv, rv);
    241  if (mExtraField) {
    242    rv = ZW_WriteData(aStream, (const char*)mExtraField.get(), mFieldLength);
    243    NS_ENSURE_SUCCESS(rv, rv);
    244  }
    245  return ZW_WriteData(aStream, mComment.get(), mComment.Length());
    246 }
    247 
    248 nsresult nsZipHeader::ReadCDSHeader(nsIInputStream* stream) {
    249  NS_ASSERTION(!mInited, "Already initalised");
    250 
    251  uint8_t buf[ZIP_CDS_HEADER_SIZE];
    252 
    253  nsresult rv = ZW_ReadData(stream, (char*)buf, ZIP_CDS_HEADER_SIZE);
    254  NS_ENSURE_SUCCESS(rv, rv);
    255 
    256  uint32_t pos = 0;
    257  uint32_t signature = READ32(buf, &pos);
    258  if (signature != ZIP_CDS_HEADER_SIGNATURE) return NS_ERROR_FILE_CORRUPTED;
    259 
    260  mVersionMade = READ16(buf, &pos);
    261  mVersionNeeded = READ16(buf, &pos);
    262  mFlags = READ16(buf, &pos);
    263  mMethod = READ16(buf, &pos);
    264  mTime = READ16(buf, &pos);
    265  mDate = READ16(buf, &pos);
    266  mCRC = READ32(buf, &pos);
    267  mCSize = READ32(buf, &pos);
    268  mUSize = READ32(buf, &pos);
    269  uint16_t namelength = READ16(buf, &pos);
    270  mFieldLength = READ16(buf, &pos);
    271  uint16_t commentlength = READ16(buf, &pos);
    272  mDisk = READ16(buf, &pos);
    273  mIAttr = READ16(buf, &pos);
    274  mEAttr = READ32(buf, &pos);
    275  mOffset = READ32(buf, &pos);
    276 
    277  if (namelength > 0) {
    278    auto field = MakeUnique<char[]>(namelength);
    279    NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
    280    rv = ZW_ReadData(stream, field.get(), namelength);
    281    NS_ENSURE_SUCCESS(rv, rv);
    282    mName.Assign(field.get(), namelength);
    283  } else
    284    mName.Truncate();
    285 
    286  if (mFieldLength > 0) {
    287    mExtraField = MakeUnique<uint8_t[]>(mFieldLength);
    288    NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
    289    rv = ZW_ReadData(stream, (char*)mExtraField.get(), mFieldLength);
    290    NS_ENSURE_SUCCESS(rv, rv);
    291  }
    292 
    293  if (commentlength > 0) {
    294    auto field = MakeUnique<char[]>(commentlength);
    295    NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
    296    rv = ZW_ReadData(stream, field.get(), commentlength);
    297    NS_ENSURE_SUCCESS(rv, rv);
    298    mComment.Assign(field.get(), commentlength);
    299  } else
    300    mComment.Truncate();
    301 
    302  mInited = true;
    303  return NS_OK;
    304 }
    305 
    306 const uint8_t* nsZipHeader::GetExtraField(uint16_t aTag, bool aLocal,
    307                                          uint16_t* aBlockSize) {
    308  const uint8_t* buf = aLocal ? mLocalExtraField.get() : mExtraField.get();
    309  uint32_t buflen = aLocal ? mLocalFieldLength : mFieldLength;
    310  uint32_t pos = 0;
    311  uint16_t tag, blocksize;
    312 
    313  while (buf && (pos + 4) <= buflen) {
    314    tag = READ16(buf, &pos);
    315    blocksize = READ16(buf, &pos);
    316 
    317    if (aTag == tag && (pos + blocksize) <= buflen) {
    318      *aBlockSize = blocksize;
    319      return buf + pos - 4;
    320    }
    321 
    322    pos += blocksize;
    323  }
    324 
    325  return nullptr;
    326 }
    327 
    328 /*
    329 * Pad extra field to align data starting position to specified size.
    330 */
    331 nsresult nsZipHeader::PadExtraField(uint32_t aOffset, uint16_t aAlignSize) {
    332  uint32_t pad_size;
    333  uint32_t pa_offset;
    334  uint32_t pa_end;
    335 
    336  // Check for range and power of 2.
    337  if (aAlignSize < 2 || aAlignSize > 32768 ||
    338      (aAlignSize & (aAlignSize - 1)) != 0) {
    339    return NS_ERROR_INVALID_ARG;
    340  }
    341 
    342  // Point to current starting data position.
    343  aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
    344 
    345  // Calculate aligned offset.
    346  pa_offset = aOffset & ~(aAlignSize - 1);
    347  pa_end = pa_offset + aAlignSize;
    348  pad_size = pa_end - aOffset;
    349  if (pad_size == 0) {
    350    return NS_OK;
    351  }
    352 
    353  // Leave enough room(at least 4 bytes) for valid values in extra field.
    354  while (pad_size < 4) {
    355    pad_size += aAlignSize;
    356  }
    357  // Extra field length is 2 bytes.
    358  if (mLocalFieldLength + pad_size > 65535) {
    359    return NS_ERROR_FAILURE;
    360  }
    361 
    362  UniquePtr<uint8_t[]> field = std::move(mLocalExtraField);
    363  uint32_t pos = mLocalFieldLength;
    364 
    365  mLocalExtraField = MakeUnique<uint8_t[]>(mLocalFieldLength + pad_size);
    366  memcpy(mLocalExtraField.get(), field.get(), mLocalFieldLength);
    367  // Use 0xFFFF as tag ID to avoid conflict with other IDs.
    368  // For more information, please read "Extensible data fields" section in:
    369  // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
    370  WRITE16(mLocalExtraField.get(), &pos, 0xFFFF);
    371  WRITE16(mLocalExtraField.get(), &pos, pad_size - 4);
    372  memset(mLocalExtraField.get() + pos, 0, pad_size - 4);
    373  mLocalFieldLength += pad_size;
    374 
    375  return NS_OK;
    376 }