Example #1
0
    private static UnixTime GetTimestamp(ZipEntry entry)
    {
        var oldTimestamp = new ZipExtraData(entry.ExtraData).GetData <OldUnixExtraData>()?.ModificationTime;

        if (oldTimestamp.HasValue)
        {
            return(oldTimestamp.Value);
        }

        // Special-case handling for unset/default timestamps to match behavior of Info-ZIP
        if (entry.DateTime == new DateTime(1980, 1, 1, 0, 0, 0, DateTimeKind.Utc))
        {
            return(entry.DateTime - TimeSpan.FromDays(1));
        }

        return(entry.DateTime);
    }
        public void ReadOverrunLong()
        {
            var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 });

            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            // Empty Tag
            bool exceptionCaught = false;

            try {
                zed.ReadLong();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            // seven bytes
            zed = new ZipExtraData(new byte[] { 1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7 });
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            exceptionCaught = false;
            try {
                zed.ReadLong();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            zed = new ZipExtraData(new byte[] { 1, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            zed.ReadLong();

            exceptionCaught = false;
            try {
                zed.ReadLong();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");
        }
        public void ReadOverrunShort()
        {
            var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 });

            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            // Empty Tag
            bool exceptionCaught = false;

            try {
                zed.ReadShort();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            // Single byte
            zed = new ZipExtraData(new byte[] { 1, 0, 1, 0, 1 });
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            exceptionCaught = false;
            try {
                zed.ReadShort();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            zed = new ZipExtraData(new byte[] { 1, 0, 2, 0, 1, 2 });
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            zed.ReadShort();

            exceptionCaught = false;
            try {
                zed.ReadShort();
            } catch (ZipException) {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");
        }
        public void UnreadCountValid()
        {
            var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 });

            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");
            Assert.AreEqual(0, zed.UnreadCount);

            // seven bytes
            zed = new ZipExtraData(new byte[] { 1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7 });
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            for (int i = 0; i < 7; ++i)
            {
                Assert.AreEqual(7 - i, zed.UnreadCount);
                zed.ReadByte();
            }

            zed.ReadByte();
            Assert.AreEqual(0, zed.UnreadCount);
        }
Example #5
0
        public void PasswordCheckingWithDateInExtraData()
        {
            var ms        = new MemoryStream();
            var checkTime = new DateTimeOffset(2010, 10, 16, 0, 3, 28, new TimeSpan(1, 0, 0));

            using (ZipOutputStream zos = new ZipOutputStream(ms))
            {
                zos.IsStreamOwner = false;
                zos.Password      = "******";
                var ze = new ZipEntry("uno");
                ze.DateTime = new DateTime(1998, 6, 5, 4, 3, 2);

                var zed = new ZipExtraData();

                zed.StartNewEntry();

                zed.AddData(1);

                TimeSpan delta   = checkTime.UtcDateTime - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime();
                var      seconds = (int)delta.TotalSeconds;
                zed.AddLeInt(seconds);
                zed.AddNewEntry(0x5455);

                ze.ExtraData = zed.GetEntryData();
                zos.PutNextEntry(ze);
                zos.WriteByte(54);
            }

            ms.Position = 0;
            using (ZipInputStream zis = new ZipInputStream(ms))
            {
                zis.Password = "******";
                ZipEntry uno     = zis.GetNextEntry();
                var      theByte = (byte)zis.ReadByte();
                Assert.AreEqual(54, theByte);
                Assert.AreEqual(-1, zis.ReadByte());                 // eof
                Assert.AreEqual(checkTime.DateTime, uno.DateTime);
            }
        }
        public void ExceedSize()
        {
            var zed = new ZipExtraData();

            byte[] buffer = new byte[65506];
            zed.AddEntry(1, buffer);
            Assert.AreEqual(65510, zed.Length);
            zed.AddEntry(2, new byte[21]);
            Assert.AreEqual(65535, zed.Length);

            bool caught = false;

            try
            {
                zed.AddEntry(3, null);
            }
            catch
            {
                caught = true;
            }

            Assert.IsTrue(caught, "Expected an exception when max size exceeded");
            Assert.AreEqual(65535, zed.Length);

            zed.Delete(2);
            Assert.AreEqual(65510, zed.Length);

            caught = false;
            try
            {
                zed.AddEntry(2, new byte[22]);
            }
            catch
            {
                caught = true;
            }
            Assert.IsTrue(caught, "Expected an exception when max size exceeded");
            Assert.AreEqual(65510, zed.Length);
        }
Example #7
0
        public void BasicOperations()
        {
            var zed = new ZipExtraData(null);
            Assert.AreEqual(0, zed.Length);

            zed = new ZipExtraData(new byte[]{1, 0, 0, 0});
            Assert.AreEqual(4, zed.Length, "A length should be 4");

            var zed2 = new ZipExtraData();
            Assert.AreEqual(0, zed2.Length);

            zed2.AddEntry(1, new byte[]{});

            var data = zed.GetEntryData();
            for (var i = 0; i < data.Length; ++i)
            {
                Assert.AreEqual(zed2.GetEntryData()[i], data[i]);
            }

            Assert.AreEqual(4, zed2.Length, "A1 length should be 4");

            var findResult = zed.Find(2);
            Assert.IsFalse(findResult, "A - Shouldnt find tag 2");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "A - Should find tag 1");
            Assert.AreEqual(0, zed.ValueLength, "A- Length of entry should be 0");
            Assert.AreEqual(-1, zed.ReadByte());
            Assert.AreEqual(0, zed.GetStreamForTag(1).Length, "A - Length of stream should be 0");

            zed = new ZipExtraData(new byte[]{1, 0, 3, 0, 1, 2, 3});
            Assert.AreEqual(7, zed.Length, "Expected a length of 7");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "B - Should find tag 1");
            Assert.AreEqual(3, zed.ValueLength, "B - Length of entry should be 3");
            for (var i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, zed.ReadByte());
            }
            Assert.AreEqual(-1, zed.ReadByte());

            var s = zed.GetStreamForTag(1);
            Assert.AreEqual(3, s.Length, "B.1 Stream length should be 3");
            for (var i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, s.ReadByte());
            }
            Assert.AreEqual(-1, s.ReadByte());

            zed = new ZipExtraData(new byte[]{1, 0, 3, 0, 1, 2, 3, 2, 0, 1, 0, 56});
            Assert.AreEqual(12, zed.Length, "Expected a length of 12");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "C.1 - Should find tag 1");
            Assert.AreEqual(3, zed.ValueLength, "C.1 - Length of entry should be 3");
            for (var i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, zed.ReadByte());
            }
            Assert.AreEqual(-1, zed.ReadByte());

            findResult = zed.Find(2);
            Assert.IsTrue(findResult, "C.2 - Should find tag 2");
            Assert.AreEqual(1, zed.ValueLength, "C.2 - Length of entry should be 1");
            Assert.AreEqual(56, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            s = zed.GetStreamForTag(2);
            Assert.AreEqual(1, s.Length);
            Assert.AreEqual(56, s.ReadByte());
            Assert.AreEqual(-1, s.ReadByte());

            zed = new ZipExtraData();
            zed.AddEntry(7, new byte[]{33, 44, 55});
            findResult = zed.Find(7);
            Assert.IsTrue(findResult, "Add.1 should find new tag");
            Assert.AreEqual(3, zed.ValueLength, "Add.1 length should be 3");
            Assert.AreEqual(33, zed.ReadByte());
            Assert.AreEqual(44, zed.ReadByte());
            Assert.AreEqual(55, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            zed.AddEntry(7, null);
            findResult = zed.Find(7);
            Assert.IsTrue(findResult, "Add.2 should find new tag");
            Assert.AreEqual(0, zed.ValueLength, "Add.2 length should be 0");

            zed.StartNewEntry();
            zed.AddData(0xae);
            zed.AddNewEntry(55);

            findResult = zed.Find(55);
            Assert.IsTrue(findResult, "Add.3 should find new tag");
            Assert.AreEqual(1, zed.ValueLength, "Add.3 length should be 1");
            Assert.AreEqual(0xae, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            zed = new ZipExtraData();
            zed.StartNewEntry();
            zed.AddLeLong(0);
            zed.AddLeLong(-4);
            zed.AddLeLong(-1);
            zed.AddLeLong(long.MaxValue);
            zed.AddLeLong(long.MinValue);
            zed.AddLeLong(0x123456789ABCDEF0);
            zed.AddLeLong(unchecked((long) 0xFEDCBA9876543210));
            zed.AddNewEntry(567);

            s = zed.GetStreamForTag(567);
            var longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(0, longValue, "Expected long value of zero");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(-4, longValue, "Expected long value of -4");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(-1, longValue, "Expected long value of -1");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(long.MaxValue, longValue, "Expected long value of MaxValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(long.MinValue, longValue, "Expected long value of MinValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(0x123456789abcdef0, longValue, "Expected long value of MinValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(unchecked((long) 0xFEDCBA9876543210), longValue, "Expected long value of MinValue");
        }
Example #8
0
 // For AES the method in the entry is 99, and the real compression method is in the extradata
 //
 private void ProcessAESExtraData(ZipExtraData extraData)
 {
     #if !NET_1_1 && !NETCF_2_0
     if (extraData.Find(0x9901))
     {
         // Set version and flag for Zipfile.CreateAndInitDecryptionStream
         versionToExtract = ZipConstants.VERSION_AES;			// Ver 5.1 = AES see "Version" getter
         // Set StrongEncryption flag for ZipFile.CreateAndInitDecryptionStream
         Flags = Flags | (int)GeneralBitFlags.StrongEncryption;
         //
         // Unpack AES extra data field see http://www.winzip.com/aes_info.htm
         int length = extraData.ValueLength;			// Data size currently 7
         if (length < 7)
             throw new ZipException("AES Extra Data Length " + length + " invalid.");
         int ver = extraData.ReadShort();			// Version number (1=AE-1 2=AE-2)
         int vendorId = extraData.ReadShort();		// 2-character vendor ID 0x4541 = "AE"
         int encrStrength = extraData.ReadByte();	// encryption strength 1 = 128 2 = 192 3 = 256
         int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file
         _aesVer = ver;
         _aesEncryptionStrength = encrStrength;
         method = (CompressionMethod)actualCompress;
     }
     else
         throw new ZipException("AES Extra Data missing");
     #else
         throw new ZipException("AES unsupported");
     #endif
 }
		/// <summary>
		/// Finishes the stream.  This will write the central directory at the
		/// end of the zip file and flush the stream.
		/// </summary>
		/// <remarks>
		/// This is automatically called when the stream is closed.
		/// </remarks>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurs.
		/// </exception>
		/// <exception cref="ZipException">
		/// Comment exceeds the maximum length<br/>
		/// Entry name exceeds the maximum length
		/// </exception>
		public override void Finish()
		{
			if (entries == null)  {
				return;
			}
			
			if (curEntry != null) {
				CloseEntry();
			}
			
			long numEntries = entries.Count;
			long sizeEntries = 0;
			
			foreach (ZipEntry entry in entries) {
				WriteLeInt(ZipConstants.CentralHeaderSignature); 
				WriteLeShort(ZipConstants.VersionMadeBy);
				WriteLeShort(entry.Version);
				WriteLeShort(entry.Flags);
				WriteLeShort((short)entry.CompressionMethodForHeader);
				WriteLeInt((int)entry.DosTime);
				WriteLeInt((int)entry.Crc);

				if ( entry.IsZip64Forced() || 
					(entry.CompressedSize >= uint.MaxValue) )
				{
					WriteLeInt(-1);
				}
				else {
					WriteLeInt((int)entry.CompressedSize);
				}

				if ( entry.IsZip64Forced() ||
					(entry.Size >= uint.MaxValue) )
				{
					WriteLeInt(-1);
				}
				else {
					WriteLeInt((int)entry.Size);
				}

				byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
				
				if (name.Length > 0xffff) {
					throw new ZipException("Name too long.");
				}
				
				ZipExtraData ed = new ZipExtraData(entry.ExtraData);

				if ( entry.CentralHeaderRequiresZip64 ) {
					ed.StartNewEntry();
					if ( entry.IsZip64Forced() ||
						(entry.Size >= 0xffffffff) )
					{
						ed.AddLeLong(entry.Size);
					}

					if ( entry.IsZip64Forced() ||
						(entry.CompressedSize >= 0xffffffff) )
					{
						ed.AddLeLong(entry.CompressedSize);
					}

					if ( entry.Offset >= 0xffffffff )
					{
						ed.AddLeLong(entry.Offset);
					}

					ed.AddNewEntry(1);
				}
				else {
					ed.Delete(1);
				}

#if !NET_1_1 && !NETCF_2_0
				if (entry.AESKeySize > 0) {
					AddExtraDataAES(entry, ed);
				}
#endif
				byte[] extra = ed.GetEntryData();
				
				byte[] entryComment = 
					(entry.Comment != null) ? 
					ZipConstants.ConvertToArray(entry.Flags, entry.Comment) :
					new byte[0];

				if (entryComment.Length > 0xffff) {
					throw new ZipException("Comment too long.");
				}
				
				WriteLeShort(name.Length);
				WriteLeShort(extra.Length);
				WriteLeShort(entryComment.Length);
				WriteLeShort(0);	// disk number
				WriteLeShort(0);	// internal file attributes
									// external file attributes

				if (entry.ExternalFileAttributes != -1) {
					WriteLeInt(entry.ExternalFileAttributes);
				} else {
					if (entry.IsDirectory) {                         // mark entry as directory (from nikolam.AT.perfectinfo.com)
						WriteLeInt(16);
					} else {
						WriteLeInt(0);
					}
				}

				if ( entry.Offset >= uint.MaxValue ) {
					WriteLeInt(-1);
				}
				else {
					WriteLeInt((int)entry.Offset);
				}
				
				if ( name.Length > 0 ) {
					baseOutputStream_.Write(name,    0, name.Length);
				}

				if ( extra.Length > 0 ) {
					baseOutputStream_.Write(extra,   0, extra.Length);
				}

				if ( entryComment.Length > 0 ) {
					baseOutputStream_.Write(entryComment, 0, entryComment.Length);
				}

				sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
			}
			
			using ( ZipHelperStream zhs = new ZipHelperStream(baseOutputStream_) ) {
				zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment);
			}

			entries = null;
		}
		/// <summary>
		/// Starts a new Zip entry. It automatically closes the previous
		/// entry if present.
		/// All entry elements bar name are optional, but must be correct if present.
		/// If the compression method is stored and the output is not patchable
		/// the compression for that entry is automatically changed to deflate level 0
		/// </summary>
		/// <param name="entry">
		/// the entry.
		/// </param>
		/// <exception cref="System.ArgumentNullException">
		/// if entry passed is null.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// if an I/O error occured.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// if stream was finished
		/// </exception>
		/// <exception cref="ZipException">
		/// Too many entries in the Zip file<br/>
		/// Entry name is too long<br/>
		/// Finish has already been called<br/>
		/// </exception>
		public void PutNextEntry(ZipEntry entry)
		{
			if ( entry == null ) {
				throw new ArgumentNullException("entry");
			}

			if (entries == null) {
				throw new InvalidOperationException("ZipOutputStream was finished");
			}
			
			if (curEntry != null) {
				CloseEntry();
			}

			if (entries.Count == int.MaxValue) {
				throw new ZipException("Too many entries for Zip file");
			}
			
			CompressionMethod method = entry.CompressionMethod;
			int compressionLevel = defaultCompressionLevel;
			
			// Clear flags that the library manages internally
			entry.Flags &= (int)GeneralBitFlags.UnicodeText;
			patchEntryHeader = false;

			bool headerInfoAvailable;

			// No need to compress - definitely no data.
			if (entry.Size == 0)
			{
				entry.CompressedSize = entry.Size;
				entry.Crc = 0;
				method = CompressionMethod.Stored;
				headerInfoAvailable = true;
			}
			else
			{
				headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc;

				// Switch to deflation if storing isnt possible.
				if (method == CompressionMethod.Stored)
				{
					if (!headerInfoAvailable)
					{
						if (!CanPatchEntries)
						{
							// Can't patch entries so storing is not possible.
							method = CompressionMethod.Deflated;
							compressionLevel = 0;
						}
					}
					else // entry.size must be > 0
					{
						entry.CompressedSize = entry.Size;
						headerInfoAvailable = entry.HasCrc;
					}
				}
			}

			if (headerInfoAvailable == false) {
				if (CanPatchEntries == false) {
					// Only way to record size and compressed size is to append a data descriptor
					// after compressed data.

					// Stored entries of this form have already been converted to deflating.
					entry.Flags |= 8;
				} else {
					patchEntryHeader = true;
				}
			}
			
			if (Password != null) {
				entry.IsCrypted = true;
				if (entry.Crc < 0) {
					// Need to append a data descriptor as the crc isnt available for use
					// with encryption, the date is used instead.  Setting the flag
					// indicates this to the decompressor.
					entry.Flags |= 8;
				}
			}

			entry.Offset = offset;
			entry.CompressionMethod = (CompressionMethod)method;
			
			curMethod = method;
			sizePatchPos = -1;
			
			if ( (useZip64_ == UseZip64.On) || ((entry.Size < 0) && (useZip64_ == UseZip64.Dynamic)) ) {
				entry.ForceZip64();
			}

			// Write the local file header
			WriteLeInt(ZipConstants.LocalHeaderSignature);
			
			WriteLeShort(entry.Version);
			WriteLeShort(entry.Flags);
			WriteLeShort((byte)entry.CompressionMethodForHeader);
			WriteLeInt((int)entry.DosTime);

			// TODO: Refactor header writing.  Its done in several places.
			if (headerInfoAvailable == true) {
				WriteLeInt((int)entry.Crc);
				if ( entry.LocalHeaderRequiresZip64 ) {
					WriteLeInt(-1);
					WriteLeInt(-1);
				}
				else {
					WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
					WriteLeInt((int)entry.Size);
				}
			} else {
				if (patchEntryHeader) {
					crcPatchPos = baseOutputStream_.Position;
				}
				WriteLeInt(0);	// Crc
				
				if ( patchEntryHeader ) {
					sizePatchPos = baseOutputStream_.Position;
				}

				// For local header both sizes appear in Zip64 Extended Information
				if ( entry.LocalHeaderRequiresZip64 || patchEntryHeader ) {
					WriteLeInt(-1);
					WriteLeInt(-1);
				}
				else {
					WriteLeInt(0);	// Compressed size
					WriteLeInt(0);	// Uncompressed size
				}
			}

			byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
			
			if (name.Length > 0xFFFF) {
				throw new ZipException("Entry name too long.");
			}

			ZipExtraData ed = new ZipExtraData(entry.ExtraData);

			if (entry.LocalHeaderRequiresZip64) {
				ed.StartNewEntry();
				if (headerInfoAvailable) {
					ed.AddLeLong(entry.Size);
					ed.AddLeLong(entry.CompressedSize);
				}
				else {
					ed.AddLeLong(-1);
					ed.AddLeLong(-1);
				}
				ed.AddNewEntry(1);

				if ( !ed.Find(1) ) {
					throw new ZipException("Internal error cant find extra data");
				}
				
				if ( patchEntryHeader ) {
					sizePatchPos = ed.CurrentReadIndex;
				}
			}
			else {
				ed.Delete(1);
			}

#if !NET_1_1 && !NETCF_2_0
			if (entry.AESKeySize > 0) {
				AddExtraDataAES(entry, ed);
			}
#endif
			byte[] extra = ed.GetEntryData();

			WriteLeShort(name.Length);
			WriteLeShort(extra.Length);

			if ( name.Length > 0 ) {
				baseOutputStream_.Write(name, 0, name.Length);
			}
			
			if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
				sizePatchPos += baseOutputStream_.Position;
			}

			if ( extra.Length > 0 ) {
				baseOutputStream_.Write(extra, 0, extra.Length);
			}
			
			offset += ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;
			// Fix offsetOfCentraldir for AES
			if (entry.AESKeySize > 0)
				offset += entry.AESOverheadSize;
			
			// Activate the entry.
			curEntry = entry;
			crc.Reset();
			if (method == CompressionMethod.Deflated) {
				deflater_.Reset();
				deflater_.SetLevel(compressionLevel);
			}
			size = 0;

			if (entry.IsCrypted) {
#if !NET_1_1 && !NETCF_2_0
				if (entry.AESKeySize > 0) {
					WriteAESHeader(entry);
				} else
#endif
				{
					if (entry.Crc < 0) {			// so testing Zip will says its ok
						WriteEncryptionHeader(entry.DosTime << 16);
					} else {
						WriteEncryptionHeader(entry.Crc);
					}
				}
			}
		}
Example #11
0
        public void UnreadCountValid()
        {
            var zed = new ZipExtraData(new byte[]{1, 0, 0, 0});
            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");
            Assert.AreEqual(0, zed.UnreadCount);

            // seven bytes
            zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7});
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            for (var i = 0; i < 7; ++i)
            {
                Assert.AreEqual(7 - i, zed.UnreadCount);
                zed.ReadByte();
            }

            zed.ReadByte();
            Assert.AreEqual(0, zed.UnreadCount);
        }
Example #12
0
        public void Skipping()
        {
            var zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7});
            Assert.AreEqual(11, zed.Length, "Length should be 11");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            Assert.AreEqual(7, zed.UnreadCount);
            Assert.AreEqual(4, zed.CurrentReadIndex);

            zed.ReadByte();
            Assert.AreEqual(6, zed.UnreadCount);
            Assert.AreEqual(5, zed.CurrentReadIndex);

            zed.Skip(1);
            Assert.AreEqual(5, zed.UnreadCount);
            Assert.AreEqual(6, zed.CurrentReadIndex);

            zed.Skip(-1);
            Assert.AreEqual(6, zed.UnreadCount);
            Assert.AreEqual(5, zed.CurrentReadIndex);

            zed.Skip(6);
            Assert.AreEqual(0, zed.UnreadCount);
            Assert.AreEqual(11, zed.CurrentReadIndex);

            var exceptionCaught = false;

            try
            {
                zed.Skip(1);
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Should fail to skip past end");

            Assert.AreEqual(0, zed.UnreadCount);
            Assert.AreEqual(11, zed.CurrentReadIndex);

            zed.Skip(-7);
            Assert.AreEqual(7, zed.UnreadCount);
            Assert.AreEqual(4, zed.CurrentReadIndex);

            try
            {
                zed.Skip(-1);
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Should fail to skip before beginning");
        }
Example #13
0
        public void ReadOverrunShort()
        {
            var zed = new ZipExtraData(new byte[]{1, 0, 0, 0});
            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            // Empty Tag
            var exceptionCaught = false;
            try
            {
                zed.ReadShort();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            // Single byte
            zed = new ZipExtraData(new byte[]{1, 0, 1, 0, 1});
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            exceptionCaught = false;
            try
            {
                zed.ReadShort();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            zed = new ZipExtraData(new byte[]{1, 0, 2, 0, 1, 2});
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            zed.ReadShort();

            exceptionCaught = false;
            try
            {
                zed.ReadShort();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");
        }
Example #14
0
        public void ReadOverrunLong()
        {
            var zed = new ZipExtraData(new byte[]{1, 0, 0, 0});
            Assert.AreEqual(4, zed.Length, "Length should be 4");
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            // Empty Tag
            var exceptionCaught = false;
            try
            {
                zed.ReadLong();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            // seven bytes
            zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7});
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            exceptionCaught = false;
            try
            {
                zed.ReadLong();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");

            zed = new ZipExtraData(new byte[]{1, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
            Assert.IsTrue(zed.Find(1), "Should find tag 1");

            zed.ReadLong();

            exceptionCaught = false;
            try
            {
                zed.ReadLong();
            }
            catch (ZipException)
            {
                exceptionCaught = true;
            }
            Assert.IsTrue(exceptionCaught, "Expected EOS exception");
        }
Example #15
0
        public void ExceedSize()
        {
            var zed = new ZipExtraData();
            var buffer = new byte[65506];
            zed.AddEntry(1, buffer);
            Assert.AreEqual(65510, zed.Length);
            zed.AddEntry(2, new byte[21]);
            Assert.AreEqual(65535, zed.Length);

            var caught = false;
            try
            {
                zed.AddEntry(3, null);
            }
            catch
            {
                caught = true;
            }
            Assert.IsTrue(caught, "Expected an exception when max size exceeded");
            Assert.AreEqual(65535, zed.Length);

            zed.Delete(2);
            Assert.AreEqual(65510, zed.Length);

            caught = false;
            try
            {
                zed.AddEntry(2, new byte[22]);
            }
            catch
            {
                caught = true;
            }
            Assert.IsTrue(caught, "Expected an exception when max size exceeded");
            Assert.AreEqual(65510, zed.Length);
        }
Example #16
0
        public void Deleting()
        {
            var zed = new ZipExtraData();
            Assert.AreEqual(0, zed.Length);

            // Tag 1 Totoal length 10
            zed.AddEntry(1, new byte[]{10, 11, 12, 13, 14, 15});
            Assert.AreEqual(10, zed.Length, "Length should be 10");
            Assert.AreEqual(10, zed.GetEntryData().Length, "Data length should be 10");

            // Tag 2 total length  9
            zed.AddEntry(2, new byte[]{20, 21, 22, 23, 24});
            Assert.AreEqual(19, zed.Length, "Length should be 19");
            Assert.AreEqual(19, zed.GetEntryData().Length, "Data length should be 19");

            // Tag 3 Total Length 6
            zed.AddEntry(3, new byte[]{30, 31});
            Assert.AreEqual(25, zed.Length, "Length should be 25");
            Assert.AreEqual(25, zed.GetEntryData().Length, "Data length should be 25");

            zed.Delete(2);
            Assert.AreEqual(16, zed.Length, "Length should be 16");
            Assert.AreEqual(16, zed.GetEntryData().Length, "Data length should be 16");

            // Tag 2 total length  9
            zed.AddEntry(2, new byte[]{20, 21, 22, 23, 24});
            Assert.AreEqual(25, zed.Length, "Length should be 25");
            Assert.AreEqual(25, zed.GetEntryData().Length, "Data length should be 25");

            zed.AddEntry(3, null);
            Assert.AreEqual(23, zed.Length, "Length should be 23");
            Assert.AreEqual(23, zed.GetEntryData().Length, "Data length should be 23");
        }
		/// <summary>
		/// Test a local header against that provided from the central directory
		/// </summary>
		/// <param name="entry">
		/// The entry to test against
		/// </param>
		/// <param name="tests">The type of <see cref="HeaderTest">tests</see> to carry out.</param>
		/// <returns>The offset of the entries data in the file</returns>
		long TestLocalHeader(ZipEntry entry, HeaderTest tests)
		{
			lock(baseStream_) 
			{
				bool testHeader = (tests & HeaderTest.Header) != 0;
				bool testData = (tests & HeaderTest.Extract) != 0;

				baseStream_.Seek(offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin);
				if ((int)ReadLEUint() != ZipConstants.LocalHeaderSignature) {
					throw new ZipException(string.Format("Wrong local header signature @{0:X}", offsetOfFirstEntry + entry.Offset));
				}

				short extractVersion = ( short )ReadLEUshort();
				short localFlags = ( short )ReadLEUshort();
				short compressionMethod = ( short )ReadLEUshort();
				short fileTime = ( short )ReadLEUshort();
				short fileDate = ( short )ReadLEUshort();
				uint crcValue = ReadLEUint();
				long compressedSize = ReadLEUint();
				long size = ReadLEUint();
				int storedNameLength = ReadLEUshort();
				int extraDataLength = ReadLEUshort();

				byte[] nameData = new byte[storedNameLength];
				StreamUtils.ReadFully(baseStream_, nameData);

				byte[] extraData = new byte[extraDataLength];
				StreamUtils.ReadFully(baseStream_, extraData);

				ZipExtraData localExtraData = new ZipExtraData(extraData);

				// Extra data / zip64 checks
				if (localExtraData.Find(1))
				{
					// 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64
					// and size or compressedSize = MaxValue, due to rogue creators.

					size = localExtraData.ReadLong();
					compressedSize = localExtraData.ReadLong();

                    if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0)
                    {
                        // These may be valid if patched later
                        if ( (size != -1) && (size != entry.Size)) {
                            throw new ZipException("Size invalid for descriptor");
                        }

                        if ((compressedSize != -1) && (compressedSize != entry.CompressedSize)) {
                            throw new ZipException("Compressed size invalid for descriptor");
                        }
                    }
                }
				else
				{
					// No zip64 extra data but entry requires it.
					if ((extractVersion >= ZipConstants.VersionZip64) &&
						(((uint)size == uint.MaxValue) || ((uint)compressedSize == uint.MaxValue)))
					{
						throw new ZipException("Required Zip64 extended information missing");
					}
				}

				if ( testData ) {
					if ( entry.IsFile ) {
						if ( !entry.IsCompressionMethodSupported() ) {
							throw new ZipException("Compression method not supported");
						}

						if ( (extractVersion > ZipConstants.VersionMadeBy)
							|| ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64)) ) {
							throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extractVersion));
						}

						if ( (localFlags & ( int )(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) != 0 ) {
							throw new ZipException("The library does not support the zip version required to extract this entry");
						}
					}
				}

                if (testHeader)
                {
                    if ((extractVersion <= 63) &&	// Ignore later versions as we dont know about them..
                        (extractVersion != 10) &&
                        (extractVersion != 11) &&
                        (extractVersion != 20) &&
                        (extractVersion != 21) &&
                        (extractVersion != 25) &&
                        (extractVersion != 27) &&
                        (extractVersion != 45) &&
                        (extractVersion != 46) &&
                        (extractVersion != 50) &&
                        (extractVersion != 51) &&
                        (extractVersion != 52) &&
                        (extractVersion != 61) &&
                        (extractVersion != 62) &&
                        (extractVersion != 63)
                        )
                    {
                        throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion));
                    }

                    // Local entry flags dont have reserved bit set on.
                    if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0)
                    {
                        throw new ZipException("Reserved bit flags cannot be set.");
                    }

                    // Encryption requires extract version >= 20
                    if (((localFlags & (int)GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20))
                    {
                        throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion));
                    }

                    // Strong encryption requires encryption flag to be set and extract version >= 50.
                    if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0)
                    {
                        if ((localFlags & (int)GeneralBitFlags.Encrypted) == 0)
                        {
                            throw new ZipException("Strong encryption flag set but encryption flag is not set");
                        }

                        if (extractVersion < 50)
                        {
                            throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion));
                        }
                    }

                    // Patched entries require extract version >= 27
                    if (((localFlags & (int)GeneralBitFlags.Patched) != 0) && (extractVersion < 27))
                    {
                        throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion));
                    }

                    // Central header flags match local entry flags.
                    if (localFlags != entry.Flags)
                    {
                        throw new ZipException("Central header/local header flags mismatch");
                    }

                    // Central header compression method matches local entry
                    if (entry.CompressionMethod != (CompressionMethod)compressionMethod)
                    {
                        throw new ZipException("Central header/local header compression method mismatch");
                    }

                    if (entry.Version != extractVersion)
                    {
                        throw new ZipException("Extract version mismatch");
                    }

                    // Strong encryption and extract version match
                    if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0)
                    {
                        if (extractVersion < 62)
                        {
                            throw new ZipException("Strong encryption flag set but version not high enough");
                        }
                    }

                    if ((localFlags & (int)GeneralBitFlags.HeaderMasked) != 0)
                    {
                        if ((fileTime != 0) || (fileDate != 0))
                        {
                            throw new ZipException("Header masked set but date/time values non-zero");
                        }
                    }

                    if ((localFlags & (int)GeneralBitFlags.Descriptor) == 0)
                    {
                        if (crcValue != (uint)entry.Crc)
                        {
                            throw new ZipException("Central header/local header crc mismatch");
                        }
                    }

                    // Crc valid for empty entry.
                    // This will also apply to streamed entries where size isnt known and the header cant be patched
                    if ((size == 0) && (compressedSize == 0))
                    {
                        if (crcValue != 0)
                        {
                            throw new ZipException("Invalid CRC for empty entry");
                        }
                    }

                    // TODO: make test more correct...  can't compare lengths as was done originally as this can fail for MBCS strings
                    // Assuming a code page at this point is not valid?  Best is to store the name length in the ZipEntry probably
                    if (entry.Name.Length > storedNameLength)
                    {
                        throw new ZipException("File name length mismatch");
                    }

                    // Name data has already been read convert it and compare.
                    string localName = ZipConstants.ConvertToStringExt(localFlags, nameData);

                    // Central directory and local entry name match
                    if (localName != entry.Name)
                    {
                        throw new ZipException("Central header and local header file name mismatch");
                    }

                    // Directories have zero actual size but can have compressed size
                    if (entry.IsDirectory)
                    {
                        if (size > 0)
                        {
                            throw new ZipException("Directory cannot have size");
                        }

                        // There may be other cases where the compressed size can be greater than this?
                        // If so until details are known we will be strict.
                        if (entry.IsCrypted)
                        {
                            if (compressedSize > ZipConstants.CryptoHeaderSize + 2)
                            {
                                throw new ZipException("Directory compressed size invalid");
                            }
                        }
                        else if (compressedSize > 2)
                        {
                            // When not compressed the directory size can validly be 2 bytes
                            // if the true size wasnt known when data was originally being written.
                            // NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes
                            throw new ZipException("Directory compressed size invalid");
                        }
                    }

                    if (!ZipNameTransform.IsValidName(localName, true))
                    {
                        throw new ZipException("Name is invalid");
                    }
                }

				// Tests that apply to both data and header.

				// Size can be verified only if it is known in the local header.
				// it will always be known in the central header.
				if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) ||
					((size > 0) || (compressedSize > 0))) {

					if (size != entry.Size) {
						throw new ZipException(
							string.Format("Size mismatch between central header({0}) and local header({1})",
								entry.Size, size));
					}

					if (compressedSize != entry.CompressedSize &&
						compressedSize != 0xFFFFFFFF && compressedSize != -1) {
						throw new ZipException(
							string.Format("Compressed size mismatch between central header({0}) and local header({1})",
							entry.CompressedSize, compressedSize));
					}
				}

				int extraLength = storedNameLength + extraDataLength;
				return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength;
			}
		}
		void WriteLocalEntryHeader(ZipUpdate update)
		{
			ZipEntry entry = update.OutEntry;

			// TODO: Local offset will require adjusting for multi-disk zip files.
			entry.Offset = baseStream_.Position;

			// TODO: Need to clear any entry flags that dont make sense or throw an exception here.
			if (update.Command != UpdateCommand.Copy) {
				if (entry.CompressionMethod == CompressionMethod.Deflated) {
					if (entry.Size == 0) {
						// No need to compress - no data.
						entry.CompressedSize = entry.Size;
						entry.Crc = 0;
						entry.CompressionMethod = CompressionMethod.Stored;
					}
				}
				else if (entry.CompressionMethod == CompressionMethod.Stored) {
					entry.Flags &= ~(int)GeneralBitFlags.Descriptor;
				}

				if (HaveKeys) {
					entry.IsCrypted = true;
					if (entry.Crc < 0) {
						entry.Flags |= (int)GeneralBitFlags.Descriptor;
					}
				}
				else {
					entry.IsCrypted = false;
				}

				switch (useZip64_) {
					case UseZip64.Dynamic:
						if (entry.Size < 0) {
							entry.ForceZip64();
						}
						break;

					case UseZip64.On:
						entry.ForceZip64();
						break;

					case UseZip64.Off:
						// Do nothing.  The entry itself may be using Zip64 independantly.
						break;
				}
			}

			// Write the local file header
			WriteLEInt(ZipConstants.LocalHeaderSignature);

			WriteLEShort(entry.Version);
			WriteLEShort(entry.Flags);

			WriteLEShort((byte)entry.CompressionMethod);
			WriteLEInt(( int )entry.DosTime);

			if ( !entry.HasCrc ) {
				// Note patch address for updating CRC later.
				update.CrcPatchOffset = baseStream_.Position;
				WriteLEInt(( int )0);
			}
			else {
				WriteLEInt(unchecked(( int )entry.Crc));
			}

			if (entry.LocalHeaderRequiresZip64) {
				WriteLEInt(-1);
				WriteLEInt(-1);
			}
			else {
				if ( (entry.CompressedSize < 0) || (entry.Size < 0) ) {
					update.SizePatchOffset = baseStream_.Position;
				}

				WriteLEInt(( int )entry.CompressedSize);
				WriteLEInt(( int )entry.Size);
			}

			byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

			if ( name.Length > 0xFFFF ) {
				throw new ZipException("Entry name too long.");
			}

			ZipExtraData ed = new ZipExtraData(entry.ExtraData);

			if ( entry.LocalHeaderRequiresZip64 ) {
				ed.StartNewEntry();

				// Local entry header always includes size and compressed size.
				// NOTE the order of these fields is reversed when compared to the normal headers!
				ed.AddLeLong(entry.Size);
				ed.AddLeLong(entry.CompressedSize);
				ed.AddNewEntry(1);
			}
			else {
				ed.Delete(1);
			}

			entry.ExtraData = ed.GetEntryData();

			WriteLEShort(name.Length);
			WriteLEShort(entry.ExtraData.Length);

			if ( name.Length > 0 ) {
				baseStream_.Write(name, 0, name.Length);
			}

			if ( entry.LocalHeaderRequiresZip64 ) {
				if ( !ed.Find(1) ) {
					throw new ZipException("Internal error cannot find extra data");
				}

				update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex;
			}

			if ( entry.ExtraData.Length > 0 ) {
				baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length);
			}
		}
		private static void AddExtraDataAES(ZipEntry entry, ZipExtraData extraData) {

			// Vendor Version: AE-1 IS 1. AE-2 is 2. With AE-2 no CRC is required and 0 is stored.
			const int VENDOR_VERSION = 2;
			// Vendor ID is the two ASCII characters "AE".
			const int VENDOR_ID = 0x4541; //not 6965;
			extraData.StartNewEntry();
			// Pack AES extra data field see http://www.winzip.com/aes_info.htm
			//extraData.AddLeShort(7);							// Data size (currently 7)
			extraData.AddLeShort(VENDOR_VERSION);				// 2 = AE-2
			extraData.AddLeShort(VENDOR_ID);					// "AE"
			extraData.AddData(entry.AESEncryptionStrength);		//  1 = 128, 2 = 192, 3 = 256
			extraData.AddLeShort((int)entry.CompressionMethod); // The actual compression method used to compress the file
			extraData.AddNewEntry(0x9901);
		}
		int WriteCentralDirectoryHeader(ZipEntry entry)
		{
			if ( entry.CompressedSize < 0 ) {
				throw new ZipException("Attempt to write central directory entry with unknown csize");
			}

			if ( entry.Size < 0 ) {
				throw new ZipException("Attempt to write central directory entry with unknown size");
			}
			
			if ( entry.Crc < 0 ) {
				throw new ZipException("Attempt to write central directory entry with unknown crc");
			}
			
			// Write the central file header
			WriteLEInt(ZipConstants.CentralHeaderSignature);

			// Version made by
			WriteLEShort(ZipConstants.VersionMadeBy);

			// Version required to extract
			WriteLEShort(entry.Version);

			WriteLEShort(entry.Flags);
			
			unchecked {
				WriteLEShort((byte)entry.CompressionMethod);
				WriteLEInt((int)entry.DosTime);
				WriteLEInt((int)entry.Crc);
			}

			if ( (entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff) ) {
				WriteLEInt(-1);
			}
			else {
				WriteLEInt((int)(entry.CompressedSize & 0xffffffff));
			}

			if ( (entry.IsZip64Forced()) || (entry.Size >= 0xffffffff) ) {
				WriteLEInt(-1);
			}
			else {
				WriteLEInt((int)entry.Size);
			}

			byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);

			if ( name.Length > 0xFFFF ) {
				throw new ZipException("Entry name is too long.");
			}

			WriteLEShort(name.Length);

			// Central header extra data is different to local header version so regenerate.
			ZipExtraData ed = new ZipExtraData(entry.ExtraData);

			if ( entry.CentralHeaderRequiresZip64 ) {
				ed.StartNewEntry();

				if ( (entry.Size >= 0xffffffff) || (useZip64_ == UseZip64.On) )
				{
					ed.AddLeLong(entry.Size);
				}

				if ( (entry.CompressedSize >= 0xffffffff) || (useZip64_ == UseZip64.On) )
				{
					ed.AddLeLong(entry.CompressedSize);
				}

				if ( entry.Offset >= 0xffffffff ) {
					ed.AddLeLong(entry.Offset);
				}

				// Number of disk on which this file starts isnt supported and is never written here.
				ed.AddNewEntry(1);
			}
			else {
				// Should have already be done when local header was added.
				ed.Delete(1);
			}

			byte[] centralExtraData = ed.GetEntryData();

			WriteLEShort(centralExtraData.Length);
			WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0);

			WriteLEShort(0);	// disk number
			WriteLEShort(0);	// internal file attributes

			// External file attributes...
			if ( entry.ExternalFileAttributes != -1 ) {
				WriteLEInt(entry.ExternalFileAttributes);
			}
			else {
				if ( entry.IsDirectory ) {
					WriteLEUint(16);
				}
				else {
					WriteLEUint(0);
				}
			}

			if ( entry.Offset >= 0xffffffff ) {
				WriteLEUint(0xffffffff);
			}
			else {
				WriteLEUint((uint)(int)entry.Offset);
			}

			if ( name.Length > 0 ) {
				baseStream_.Write(name, 0, name.Length);
			}

			if ( centralExtraData.Length > 0 ) {
				baseStream_.Write(centralExtraData, 0, centralExtraData.Length);
			}

			byte[] rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : new byte[0];

			if ( rawComment.Length > 0 ) {
				baseStream_.Write(rawComment, 0, rawComment.Length);
			}

			return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length;
		}
Example #21
0
        /// <summary>
        /// Process extra data fields updating the entry based on the contents.
        /// </summary>
        /// <param name="localHeader">True if the extra data fields should be handled
        /// for a local header, rather than for a central header.
        /// </param>
        internal void ProcessExtraData(bool localHeader)
        {
            ZipExtraData extraData = new ZipExtraData(this.extra);

            if (extraData.Find(0x0001))
            {
                // Version required to extract is ignored here as some archivers dont set it correctly
                // in theory it should be version 45 or higher

                // The recorded size will change but remember that this is zip64.
                forceZip64_ = true;

                if (extraData.ValueLength < 4)
                {
                    throw new ZipException("Extra data extended Zip64 information length is invalid");
                }

                if (localHeader || (size == uint.MaxValue))
                {
                    size = (ulong)extraData.ReadLong();
                }

                if (localHeader || (compressedSize == uint.MaxValue))
                {
                    compressedSize = (ulong)extraData.ReadLong();
                }

                if (!localHeader && (offset == uint.MaxValue))
                {
                    offset = extraData.ReadLong();
                }

                // Disk number on which file starts is ignored
            }
            else
            {
                if (
                    ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) &&
                    ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
                )
                {
                    throw new ZipException("Zip64 Extended information required but is missing.");
                }
            }

            if (extraData.Find(10))
            {
                // No room for any tags.
                if (extraData.ValueLength < 4)
                {
                    throw new ZipException("NTFS Extra data invalid");
                }

                extraData.ReadInt(); // Reserved

                while (extraData.UnreadCount >= 4)
                {
                    int ntfsTag = extraData.ReadShort();
                    int ntfsLength = extraData.ReadShort();
                    if (ntfsTag == 1)
                    {
                        if (ntfsLength >= 24)
                        {
                            long lastModification = extraData.ReadLong();
                            long lastAccess = extraData.ReadLong();
                            long createTime = extraData.ReadLong();

                            DateTime = System.DateTime.FromFileTime(lastModification);
                        }
                        break;
                    }
                    else
                    {
                        // An unknown NTFS tag so simply skip it.
                        extraData.Skip(ntfsLength);
                    }
                }
            }
            else if (extraData.Find(0x5455))
            {
                int length = extraData.ValueLength;
                int flags = extraData.ReadByte();

                // Can include other times but these are ignored.  Length of data should
                // actually be 1 + 4 * no of bits in flags.
                if (((flags & 1) != 0) && (length >= 5))
                {
                    int iTime = extraData.ReadInt();

                    DateTime = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
                        new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
                }
            }
            if (method == CompressionMethod.WinZipAES)
            {
                ProcessAESExtraData(extraData);
            }
        }
Example #22
0
        /// <summary>
        /// Process extra data fields updating the entry based on the contents.
        /// </summary>
        /// <param name="localHeader">True if the extra data fields should be handled
        /// for a local header, rather than for a central header.
        /// </param>
        internal void ProcessExtraData(bool localHeader)
        {
            ZipExtraData extraData = new ZipExtraData(this.extra);

            if (extraData.Find(0x0001))
            {
                if ((versionToExtract & 0xff) < ZipConstants.VersionZip64)
                {
                    throw new ZipException("Zip64 Extended information found but version is not valid");
                }

                // The recorded size will change but remember that this is zip64.
                forceZip64_ = true;

                if (extraData.ValueLength < 4)
                {
                    throw new ZipException("Extra data extended Zip64 information length is invalid");
                }

                if (localHeader || (size == uint.MaxValue))
                {
                    size = (ulong)extraData.ReadLong();
                }

                if (localHeader || (compressedSize == uint.MaxValue))
                {
                    compressedSize = (ulong)extraData.ReadLong();
                }

                if (!localHeader && (offset == uint.MaxValue))
                {
                    offset = extraData.ReadLong();
                }
            }
            else
            {
                if (
                    ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) &&
                    ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
                    )
                {
                    throw new ZipException("Zip64 Extended information required but is missing.");
                }
            }

            if (extraData.Find(10))
            {
                // No room for any tags.
                if (extraData.ValueLength < 8)
                {
                    throw new ZipException("NTFS Extra data invalid");
                }

                extraData.ReadInt();                 // Reserved

                while (extraData.UnreadCount >= 4)
                {
                    int ntfsTag    = extraData.ReadShort();
                    int ntfsLength = extraData.ReadShort();
                    if (ntfsTag == 1)
                    {
                        if (ntfsLength >= 24)
                        {
                            long lastModification = extraData.ReadLong();
                            long lastAccess       = extraData.ReadLong();
                            long createTime       = extraData.ReadLong();

                            DateTime = System.DateTime.FromFileTime(lastModification);
                        }
                        break;
                    }
                    else
                    {
                        // An unknown NTFS tag so simply skip it.
                        extraData.Skip(ntfsLength);
                    }
                }
            }
            else if (extraData.Find(0x5455))
            {
                int length = extraData.ValueLength;
                int flags  = extraData.ReadByte();

                // Can include other times but these are ignored.  Length of data should
                // actually be 1 + 4 * no of bits in flags.
                if (((flags & 1) != 0) && (length >= 5))
                {
                    int iTime = extraData.ReadInt();

                    DateTime = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
                                new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
                }
            }
        }
Example #23
0
        /// <summary>
        /// Unzip a zip file, treating paths appropriately for the file system irrespective of path style in central directory.
        /// </summary>
        /// <param name="zipFile">
        /// The zip file.
        /// </param>
        /// <param name="directory">
        /// The directory.
        /// </param>
        /// <exception cref="System.ArgumentException">
        /// Value cannot be null or empty - directory
        ///     or
        ///     Value cannot be null or empty - zipFile
        /// </exception>
        public static void Unzip(string zipFile, string directory)
        {
            if (!File.Exists(zipFile))
            {
                throw new FileNotFoundException("Cannot find zip file", zipFile);
            }

            if (string.IsNullOrWhiteSpace(directory))
            {
                throw new ArgumentException("Value cannot be null or empty", nameof(directory));
            }

            if (string.IsNullOrWhiteSpace(zipFile))
            {
                throw new ArgumentException("Value cannot be null or empty", nameof(zipFile));
            }

            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            using (var archive = new ZipInputStream(File.OpenRead(zipFile)))
            {
                const int BufSize = 4096;
                var       buffer  = new byte[BufSize];
                ZipEntry  entry;

                while ((entry = archive.GetNextEntry()) != null)
                {
                    // Convert entry path to that expected by local file system.
                    var targetPath = Path.Combine(
                        directory,
                        IsWindows ? entry.Name.Replace('/', '\\') : entry.Name.Replace('\\', '/'));

                    if (entry.ExtraData != null && entry.ExtraData.Any())
                    {
                        var ed = new ZipExtraData(entry.ExtraData);

                        // TODO - Use this
                        var unixData     = ed.GetData <ExtendedUnixData>();
                        var unixUserData = ed.GetData <UnixExtraType3>();
                    }

                    if (entry.IsDirectory)
                    {
                        if (Environment.UserInteractive)
                        {
                            Console.WriteLine($"Creating   {Path.GetFullPath(targetPath)}");
                        }

                        CreateDirectoryIfNotExists(targetPath);
                    }
                    else
                    {
                        // File entry - expand it.
                        CreateDirectoryIfNotExists(Path.GetDirectoryName(targetPath));

                        if (Environment.UserInteractive)
                        {
                            Console.WriteLine($"Extracting {Path.GetFullPath(targetPath)}");
                        }

                        using (var fs = File.OpenWrite(targetPath))
                        {
                            while (true)
                            {
                                var bytesRead = archive.Read(buffer, 0, BufSize);

                                if (bytesRead == 0)
                                {
                                    break;
                                }

                                fs.Write(buffer, 0, bytesRead);
                            }

                            fs.Flush();
                        }
                    }
                }
            }
        }
        public void BasicOperations()
        {
            var zed = new ZipExtraData(null);

            Assert.AreEqual(0, zed.Length);

            zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 });
            Assert.AreEqual(4, zed.Length, "A length should be 4");

            var zed2 = new ZipExtraData();

            Assert.AreEqual(0, zed2.Length);

            zed2.AddEntry(1, new byte[] { });

            byte[] data = zed.GetEntryData();
            for (int i = 0; i < data.Length; ++i)
            {
                Assert.AreEqual(zed2.GetEntryData()[i], data[i]);
            }

            Assert.AreEqual(4, zed2.Length, "A1 length should be 4");

            bool findResult = zed.Find(2);

            Assert.IsFalse(findResult, "A - Shouldnt find tag 2");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "A - Should find tag 1");
            Assert.AreEqual(0, zed.ValueLength, "A- Length of entry should be 0");
            Assert.AreEqual(-1, zed.ReadByte());
            Assert.AreEqual(0, zed.GetStreamForTag(1).Length, "A - Length of stream should be 0");

            zed = new ZipExtraData(new byte[] { 1, 0, 3, 0, 1, 2, 3 });
            Assert.AreEqual(7, zed.Length, "Expected a length of 7");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "B - Should find tag 1");
            Assert.AreEqual(3, zed.ValueLength, "B - Length of entry should be 3");
            for (int i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, zed.ReadByte());
            }
            Assert.AreEqual(-1, zed.ReadByte());

            Stream s = zed.GetStreamForTag(1);

            Assert.AreEqual(3, s.Length, "B.1 Stream length should be 3");
            for (int i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, s.ReadByte());
            }
            Assert.AreEqual(-1, s.ReadByte());

            zed = new ZipExtraData(new byte[] { 1, 0, 3, 0, 1, 2, 3, 2, 0, 1, 0, 56 });
            Assert.AreEqual(12, zed.Length, "Expected a length of 12");

            findResult = zed.Find(1);
            Assert.IsTrue(findResult, "C.1 - Should find tag 1");
            Assert.AreEqual(3, zed.ValueLength, "C.1 - Length of entry should be 3");
            for (int i = 1; i <= 3; ++i)
            {
                Assert.AreEqual(i, zed.ReadByte());
            }
            Assert.AreEqual(-1, zed.ReadByte());

            findResult = zed.Find(2);
            Assert.IsTrue(findResult, "C.2 - Should find tag 2");
            Assert.AreEqual(1, zed.ValueLength, "C.2 - Length of entry should be 1");
            Assert.AreEqual(56, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            s = zed.GetStreamForTag(2);
            Assert.AreEqual(1, s.Length);
            Assert.AreEqual(56, s.ReadByte());
            Assert.AreEqual(-1, s.ReadByte());

            zed = new ZipExtraData();
            zed.AddEntry(7, new byte[] { 33, 44, 55 });
            findResult = zed.Find(7);
            Assert.IsTrue(findResult, "Add.1 should find new tag");
            Assert.AreEqual(3, zed.ValueLength, "Add.1 length should be 3");
            Assert.AreEqual(33, zed.ReadByte());
            Assert.AreEqual(44, zed.ReadByte());
            Assert.AreEqual(55, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            zed.AddEntry(7, null);
            findResult = zed.Find(7);
            Assert.IsTrue(findResult, "Add.2 should find new tag");
            Assert.AreEqual(0, zed.ValueLength, "Add.2 length should be 0");

            zed.StartNewEntry();
            zed.AddData(0xae);
            zed.AddNewEntry(55);

            findResult = zed.Find(55);
            Assert.IsTrue(findResult, "Add.3 should find new tag");
            Assert.AreEqual(1, zed.ValueLength, "Add.3 length should be 1");
            Assert.AreEqual(0xae, zed.ReadByte());
            Assert.AreEqual(-1, zed.ReadByte());

            zed = new ZipExtraData();
            zed.StartNewEntry();
            zed.AddLeLong(0);
            zed.AddLeLong(-4);
            zed.AddLeLong(-1);
            zed.AddLeLong(long.MaxValue);
            zed.AddLeLong(long.MinValue);
            zed.AddLeLong(0x123456789ABCDEF0);
            zed.AddLeLong(unchecked ((long)0xFEDCBA9876543210));
            zed.AddNewEntry(567);

            s = zed.GetStreamForTag(567);
            long longValue = ReadLong(s);

            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(0, longValue, "Expected long value of zero");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(-4, longValue, "Expected long value of -4");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(-1, longValue, "Expected long value of -1");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(long.MaxValue, longValue, "Expected long value of MaxValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(long.MinValue, longValue, "Expected long value of MinValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(0x123456789abcdef0, longValue, "Expected long value of MinValue");

            longValue = ReadLong(s);
            Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch");
            Assert.AreEqual(unchecked ((long)0xFEDCBA9876543210), longValue, "Expected long value of MinValue");
        }
		// Write the local file header
		// TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
		void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData) 
		{
			CompressionMethod method = entry.CompressionMethod;
			bool headerInfoAvailable = true; // How to get this?
			bool patchEntryHeader = false;

			WriteLEInt(ZipConstants.LocalHeaderSignature);
			
			WriteLEShort(entry.Version);
			WriteLEShort(entry.Flags);
			WriteLEShort((byte)method);
			WriteLEInt((int)entry.DosTime);

			if (headerInfoAvailable == true) {
				WriteLEInt((int)entry.Crc);
				if ( entry.LocalHeaderRequiresZip64 ) {
					WriteLEInt(-1);
					WriteLEInt(-1);
				}
				else {
					WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
					WriteLEInt((int)entry.Size);
				}
			} else {
				if (patchData != null) {
					patchData.CrcPatchOffset = stream_.Position;
				}
				WriteLEInt(0);	// Crc
				
				if ( patchData != null ) {
					patchData.SizePatchOffset = stream_.Position;
				}

				// For local header both sizes appear in Zip64 Extended Information
				if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
					WriteLEInt(-1);
					WriteLEInt(-1);
				}
				else {
					WriteLEInt(0);	// Compressed size
					WriteLEInt(0);	// Uncompressed size
				}
			}

			byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
			
			if (name.Length > 0xFFFF) {
				throw new ZipException("Entry name too long.");
			}

			ZipExtraData ed = new ZipExtraData(entry.ExtraData);

			if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
				ed.StartNewEntry();
				if (headerInfoAvailable) {
					ed.AddLeLong(entry.Size);
					ed.AddLeLong(entry.CompressedSize);
				}
				else {
					ed.AddLeLong(-1);
					ed.AddLeLong(-1);
				}
				ed.AddNewEntry(1);

				if ( !ed.Find(1) ) {
					throw new ZipException("Internal error cant find extra data");
				}
				
				if ( patchData != null ) {
					patchData.SizePatchOffset = ed.CurrentReadIndex;
				}
			}
			else {
				ed.Delete(1);
			}
			
			byte[] extra = ed.GetEntryData();

			WriteLEShort(name.Length);
			WriteLEShort(extra.Length);

			if ( name.Length > 0 ) {
				stream_.Write(name, 0, name.Length);
			}
			
			if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
				patchData.SizePatchOffset += stream_.Position;
			}

			if ( extra.Length > 0 ) {
				stream_.Write(extra, 0, extra.Length);
			}
		}
Example #26
0
        public void PasswordCheckingWithDateInExtraData()
        {
            var ms = new MemoryStream();
            var checkTime = new DateTime(2010, 10, 16, 0, 3, 28);

            using (var zos = new ZipOutputStream(ms))
            {
                zos.IsStreamOwner = false;
                zos.Password = "******";
                var ze = new ZipEntry("uno");
                ze.DateTime = new DateTime(1998, 6, 5, 4, 3, 2);

                var zed = new ZipExtraData();

                zed.StartNewEntry();

                zed.AddData(1);

                var delta = checkTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime();
                var seconds = (int) delta.TotalSeconds;
                zed.AddLeInt(seconds);
                zed.AddNewEntry(0x5455);

                ze.ExtraData = zed.GetEntryData();
                zos.PutNextEntry(ze);
                zos.WriteByte(54);
            }

            ms.Position = 0;
            using (var zis = new ZipInputStream(ms))
            {
                zis.Password = "******";
                var uno = zis.GetNextEntry();
                var theByte = (byte) zis.ReadByte();
                Assert.AreEqual(54, theByte);
                Assert.AreEqual(-1, zis.ReadByte());
                Assert.AreEqual(checkTime, uno.DateTime);
            }
        }