public ZipEntry PutNextEntry(string entryName) { if (string.IsNullOrEmpty(entryName)) { throw new ArgumentNullException("entryName"); } if (_disposed) { _exceptionPending = true; throw new InvalidOperationException("The stream has been closed."); } _FinishCurrentEntry(); _currentEntry = ZipEntry.CreateForZipOutputStream(entryName); _currentEntry._container = new ZipContainer(this); _currentEntry._BitField |= 8; _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now); _currentEntry.CompressionLevel = CompressionLevel; _currentEntry.CompressionMethod = CompressionMethod; _currentEntry.Password = _password; _currentEntry.Encryption = Encryption; _currentEntry.AlternateEncoding = AlternateEncoding; _currentEntry.AlternateEncodingUsage = AlternateEncodingUsage; if (entryName.EndsWith("/")) { _currentEntry.MarkAsDirectory(); } _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0); _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0); InsureUniqueEntry(_currentEntry); _needToWriteEntryHeader = true; return(_currentEntry); }
// private string DictionaryKeyForEntry(ZipEntry ze1) // { // var filename = SharedUtilities.NormalizePathForUseInZipFile(ze1.FileName); // return filename; // } /// <summary> /// Creates a directory in the zip archive. /// </summary> /// /// <remarks> /// /// <para> /// Use this when you want to create a directory in the archive but there is /// no corresponding filesystem representation for that directory. /// </para> /// /// <para> /// You will probably not need to do this in your code. One of the only times /// you will want to do this is if you want an empty directory in the zip /// archive. The reason: if you add a file to a zip archive that is stored /// within a multi-level directory, all of the directory tree is implicitly /// created in the zip archive. /// </para> /// /// </remarks> /// /// <param name="directoryNameInArchive"> /// The name of the directory to create in the archive. /// </param> /// <returns>The <c>ZipEntry</c> added.</returns> public ZipEntry AddDirectoryByName(string directoryNameInArchive) { // workitem 9073 ZipEntry dir = ZipEntry.CreateFromNothing(directoryNameInArchive); dir._container = new ZipContainer(this); dir.MarkAsDirectory(); dir.AlternateEncoding = this.AlternateEncoding; // workitem 8984 dir.AlternateEncodingUsage = this.AlternateEncodingUsage; dir.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now); dir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes; dir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes; dir._Source = ZipEntrySource.Stream; //string key = DictionaryKeyForEntry(dir); InternalAddEntry(dir.FileName, dir); AfterAddEntry(dir); return(dir); }
// build the TOC by reading each entry in the file. private static void ReadIntoInstance_Orig(ZipFile zf) { zf.OnReadStarted(); //zf._entries = new System.Collections.Generic.List<ZipEntry>(); zf._entries = new System.Collections.Generic.Dictionary <String, ZipEntry>(); ZipEntry e; if (zf.Verbose) { if (zf.Name == null) { zf.StatusMessageTextWriter.WriteLine("Reading zip from stream..."); } else { zf.StatusMessageTextWriter.WriteLine("Reading zip {0}...", zf.Name); } } // work item 6647: PK00 (packed to removable disk) bool firstEntry = true; ZipContainer zc = new ZipContainer(zf); while ((e = ZipEntry.ReadEntry(zc, firstEntry)) != null) { if (zf.Verbose) { zf.StatusMessageTextWriter.WriteLine(" {0}", e.FileName); } zf._entries.Add(e.FileName, e); firstEntry = false; } // read the zipfile's central directory structure here. // workitem 9912 // But, because it may be corrupted, ignore errors. try { ZipEntry de; while ((de = ZipEntry.ReadDirEntry(zf)) != null) { // Housekeeping: Since ZipFile exposes ZipEntry elements in the enumerator, // we need to copy the comment that we grab from the ZipDirEntry // into the ZipEntry, so the application can access the comment. // Also since ZipEntry is used to Write zip files, we need to copy the // file attributes to the ZipEntry as appropriate. ZipEntry e1 = zf._entries[de.FileName]; if (e1 != null) { e1._Comment = de.Comment; if (de.IsDirectory) { e1.MarkAsDirectory(); } } } // workitem 8299 if (zf._locEndOfCDS > 0) { zf.ReadStream.Seek(zf._locEndOfCDS, SeekOrigin.Begin); } ReadCentralDirectoryFooter(zf); if (zf.Verbose && !String.IsNullOrEmpty(zf.Comment)) { zf.StatusMessageTextWriter.WriteLine("Zip file Comment: {0}", zf.Comment); } } catch { } zf.OnReadCompleted(); }
internal static ZipEntry ReadDirEntry(ZipFile zf, Dictionary <String, Object> previouslySeen) { System.IO.Stream s = zf.ReadStream; System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always) ? zf.AlternateEncoding : ZipFile.DefaultEncoding; while (true) { int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); // return null if this is not a local file header signature if (IsNotValidZipDirEntrySig(signature)) { s.Seek(-4, System.IO.SeekOrigin.Current); // Getting "not a ZipDirEntry signature" here is not always wrong or an // error. This can happen when walking through a zipfile. After the // last ZipDirEntry, we expect to read an // EndOfCentralDirectorySignature. When we get this is how we know // we've reached the end of the central directory. if (signature != ZipConstants.EndOfCentralDirectorySignature && signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && signature != ZipConstants.ZipEntrySignature // workitem 8299 ) { throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); } return(null); } int bytesRead = 42 + 4; byte[] block = new byte[42]; int n = s.Read(block, 0, block.Length); if (n != block.Length) { return(null); } int i = 0; ZipEntry zde = new ZipEntry(); zde.AlternateEncoding = expectedEncoding; zde._Source = ZipEntrySource.ZipFile; zde._container = new ZipContainer(zf); unchecked { zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); zde._BitField = (short)(block[i++] + block[i++] * 256); zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); zde._timestamp |= ZipEntryTimestamp.DOS; zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); } // preserve zde._CompressionMethod_FromZipFile = zde._CompressionMethod; zde._filenameLength = (short)(block[i++] + block[i++] * 256); zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); zde._commentLength = (short)(block[i++] + block[i++] * 256); zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); // workitem 7801 zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); block = new byte[zde._filenameLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } // workitem 10330 // insure unique entry names while (!zf.IgnoreDuplicateFiles && previouslySeen.ContainsKey(zde._FileNameInArchive)) { zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive); zde._metadataChanged = true; } if (zde.AttributesIndicateDirectory) { zde.MarkAsDirectory(); // may append a slash to filename if nec. } // workitem 6898 else if (zde._FileNameInArchive.EndsWith("/")) { zde.MarkAsDirectory(); } zde._CompressedFileDataSize = zde._CompressedSize; if ((zde._BitField & 0x01) == 0x01) { // this may change after processing the Extra field zde._Encryption_FromZipFile = zde._Encryption = EncryptionAlgorithm.PkzipWeak; zde._sourceIsEncrypted = true; } if (zde._extraFieldLength > 0) { zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || zde._UncompressedSize == 0xFFFFFFFF || zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64); bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); zde._CompressedFileDataSize = zde._CompressedSize; } // we've processed the extra field, so we know the encryption method is set now. if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) { // the "encryption header" of 12 bytes precedes the file data zde._CompressedFileDataSize -= 12; } // tally the trailing descriptor if ((zde._BitField & 0x0008) == 0x0008) { // sig, CRC, Comp and Uncomp sizes if (zde._InputUsesZip64) { zde._LengthOfTrailer += 24; } else { zde._LengthOfTrailer += 16; } } // workitem 12744 zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800) ? System.Text.Encoding.UTF8 :expectedEncoding; zde.AlternateEncodingUsage = ZipOption.Always; if (zde._commentLength > 0) { block = new byte[zde._commentLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } } //zde._LengthOfDirEntry = bytesRead; if (zf.IgnoreDuplicateFiles && previouslySeen.ContainsKey(zde._FileNameInArchive)) { continue; } return(zde); } }
/// <summary> /// Reads one entry from the zip directory structure in the zip file. /// </summary> /// <param name="zf"> /// The zipfile for which a directory entry will be read. From this param, the /// method gets the ReadStream and the expected text encoding /// (ProvisionalAlternateEncoding) which is used if the entry is not marked /// UTF-8. /// </param> /// <returns>the entry read from the archive.</returns> internal static ZipEntry ReadDirEntry(ZipFile zf) { System.IO.Stream s = zf.ReadStream; System.Text.Encoding expectedEncoding = zf.ProvisionalAlternateEncoding; int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); // return null if this is not a local file header signature if (IsNotValidZipDirEntrySig(signature)) { s.Seek(-4, System.IO.SeekOrigin.Current); // workitem 10178 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); // Getting "not a ZipDirEntry signature" here is not always wrong or an // error. This can happen when walking through a zipfile. After the // last ZipDirEntry, we expect to read an // EndOfCentralDirectorySignature. When we get this is how we know // we've reached the end of the central directory. if (signature != ZipConstants.EndOfCentralDirectorySignature && signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && signature != ZipConstants.ZipEntrySignature // workitem 8299 ) { throw new BadReadException(String.Format(" ZipEntry::ReadDirEntry(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); } return null; } int bytesRead = 42 + 4; byte[] block = new byte[42]; int n = s.Read(block, 0, block.Length); if (n != block.Length) return null; int i = 0; ZipEntry zde = new ZipEntry(); zde.ProvisionalAlternateEncoding = expectedEncoding; zde._Source = ZipEntrySource.ZipFile; zde._container = new ZipContainer(zf); unchecked { zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); zde._BitField = (short)(block[i++] + block[i++] * 256); zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); zde._timestamp |= ZipEntryTimestamp.DOS; zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); } // preserve zde._CompressionMethod_FromZipFile = zde._CompressionMethod; zde._filenameLength = (short)(block[i++] + block[i++] * 256); zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); zde._commentLength = (short)(block[i++] + block[i++] * 256); zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); // workitem 7801 zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); block = new byte[zde._filenameLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } // Console.WriteLine("\nEntry : {0}", zde._LocalFileName); // Console.WriteLine(" V Madeby/Needed: 0x{0:X4} / 0x{1:X4}", zde._VersionMadeBy, zde._VersionNeeded); // Console.WriteLine(" BitField/Compression: 0x{0:X4} / 0x{1:X4}", zde._BitField, zde._CompressionMethod); // Console.WriteLine(" Lastmod: {0}", zde._LastModified.ToString("u")); // Console.WriteLine(" CRC: 0x{0:X8}", zde._Crc32); // Console.WriteLine(" Comp / Uncomp: 0x{0:X8} ({0}) 0x{1:X8} ({1})", zde._CompressedSize, zde._UncompressedSize); //zde._FileNameInArchive = zde._LocalFileName; if (zde.AttributesIndicateDirectory) zde.MarkAsDirectory(); // may append a slash to filename if nec. // workitem 6898 else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory(); zde._CompressedFileDataSize = zde._CompressedSize; if ((zde._BitField & 0x01) == 0x01) { zde._Encryption_FromZipFile = zde._Encryption = EncryptionAlgorithm.PkzipWeak; // this may change after processing the Extra field zde._sourceIsEncrypted = true; } if (zde._extraFieldLength > 0) { zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || zde._UncompressedSize == 0xFFFFFFFF || zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64); bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); zde._CompressedFileDataSize = zde._CompressedSize; } // we've processed the extra field, so we know the encryption method is set now. if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) { // the "encryption header" of 12 bytes precedes the file data zde._CompressedFileDataSize -= 12; } #if AESCRYPTO else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || zde.Encryption == EncryptionAlgorithm.WinZipAes256) { zde._CompressedFileDataSize = zde.CompressedSize - (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10); zde._LengthOfTrailer = 10; } #endif // tally the trailing descriptor if ((zde._BitField & 0x0008) == 0x0008) { // sig, CRC, Comp and Uncomp sizes if (zde._InputUsesZip64) zde._LengthOfTrailer += 24; else zde._LengthOfTrailer += 16; } if (zde._commentLength > 0) { block = new byte[zde._commentLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } } //zde._LengthOfDirEntry = bytesRead; return zde; }
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; // change for workitem 8098 ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream); bytesRead += 4; // Return false if this is not a local file header signature. if (ZipEntry.IsNotValidSig(signature)) { // Getting "not a ZipEntry signature" is not always wrong or an error. // This will happen after the last entry in a zipfile. In that case, we // expect to read : // a ZipDirEntry signature (if a non-empty zip file) or // a ZipConstants.EndOfCentralDirectorySignature. // // Anything else is a surprise. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature // workitem 10178 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(" ZipEntry::ReadHeader(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); } return false; } byte[] block = new byte[26]; int n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != block.Length) return false; bytesRead += n; int i = 0; ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); ze._BitField = (Int16)(block[i++] + block[i++] * 256); ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; // transform the time data into something usable (a DateTime) ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); ze._timestamp |= ZipEntryTimestamp.DOS; if ((ze._BitField & 0x01) == 0x01) { ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field ze._sourceIsEncrypted = true; } // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and // CRC values are not true values; the true values will follow the entry data. // But, regardless of the status of bit 3 in the bitfield, the slots for // the three amigos may contain marker values for ZIP64. So we must read them. { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); if ((uint)ze._CompressedSize == 0xFFFFFFFF || (uint)ze._UncompressedSize == 0xFFFFFFFF) ze._InputUsesZip64 = true; } Int16 filenameLength = (short)(block[i++] + block[i++] * 256); Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); block = new byte[filenameLength]; n = ze.ArchiveStream.Read(block, 0, block.Length); bytesRead += n; // if the UTF8 bit is set for this entry, override the encoding the application requested. ze._actualEncoding = ((ze._BitField & 0x0800) == 0x0800) ? System.Text.Encoding.UTF8 : defaultEncoding; // need to use this form of GetString() for .NET CF ze._FileNameInArchive = ze._actualEncoding.GetString(block, 0, block.Length); // when creating an entry by reading, the LocalFileName is the same as the FileNameInArchive // No, on second thought, I think it should be empty (null). //ze._LocalFileName = ze._FileNameInArchive; // workitem 6898 if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory(); bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); ze._LengthOfTrailer = 0; // workitem 6607 - don't read for directories // actually get the compressed size and CRC if necessary if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) { // This descriptor exists only if bit 3 of the general // purpose bit flag is set (see below). It is byte aligned // and immediately follows the last byte of compressed data, // as well as any encryption trailer, as with AES. // This descriptor is used only when it was not possible to // seek in the output .ZIP file, e.g., when the output .ZIP file // was standard output or a non-seekable device. For ZIP64(tm) format // archives, the compressed and uncompressed sizes are 8 bytes each. // workitem 8098: ok (restore) long posn = ze.ArchiveStream.Position; // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and // a consistent data record after that. To be consistent, the data record must // indicate the length of the entry data. bool wantMore = true; long SizeOfDataRead = 0; int tries = 0; while (wantMore) { tries++; // We call the FindSignature shared routine to find the specified signature // in the already-opened zip archive, starting from the current cursor // position in that filestream. If we cannot find the signature, then the // routine returns -1, and the ReadHeader() method returns false, // indicating we cannot read a legal entry header. If we have found it, // then the FindSignature() method returns the number of bytes in the // stream we had to seek forward, to find the sig. We need this to // determine if the zip entry is valid, later. if (ze._container.ZipFile != null) ze._container.ZipFile.OnReadBytes(ze); long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) return false; // total size of data read (through all loops of this). SizeOfDataRead += d; if (ze._InputUsesZip64) { // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) block = new byte[20]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 20) return false; // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._UncompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes } else { // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) block = new byte[12]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 12) return false; // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes } wantMore = (SizeOfDataRead != ze._CompressedSize); if (wantMore) { // Seek back to un-read the last 12 bytes - maybe THEY contain // the ZipEntryDataDescriptorSignature. // (12 bytes for the CRC, Comp and Uncomp size.) ze.ArchiveStream.Seek(-12, SeekOrigin.Current); // workitem 10178 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } // seek back to previous position, to prepare to read file data // workitem 8098: ok (restore) ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); // workitem 10178 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); } ze._CompressedFileDataSize = ze._CompressedSize; // bit 0 set indicates that some kind of encryption is in use if ((ze._BitField & 0x01) == 0x01) { #if AESCRYPTO if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || ze.Encryption == EncryptionAlgorithm.WinZipAes256) { int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile); // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream); bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) // according to WinZip, the CompressedSize includes the AES Crypto framing data. ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata; ze._LengthOfTrailer += 10; // MAC } else #endif { // read in the header data for "weak" encryption ze._WeakEncryptionHeader = new byte[12]; bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); // decrease the filedata size by 12 bytes ze._CompressedFileDataSize -= 12; } } // Remember the size of the blob for this entry. // We also have the starting position in the stream for this entry. ze._LengthOfHeader = bytesRead; ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; // We've read in the regular entry header, the extra field, and any encryption // header. The pointer in the file is now at the start of the filedata, which is // potentially compressed and encrypted. Just ahead in the file, there are // _CompressedFileDataSize bytes of data, followed by potentially a non-zero length // trailer, consisting of optionally, some encryption stuff (10 byte MAC for AES), // and the bit-3 trailer (16 or 24 bytes). return true; }
/// <summary> /// Specify the name of the next entry that will be written to the zip file. /// </summary> /// /// <remarks> /// <para> /// Call this method just before calling <see cref="Write(byte[], int, int)"/>, to /// specify the name of the entry that the next set of bytes written to /// the <c>ZipOutputStream</c> belongs to. All subsequent calls to <c>Write</c>, /// until the next call to <c>PutNextEntry</c>, /// will be inserted into the named entry in the zip file. /// </para> /// /// <para> /// If the <paramref name="entryName"/> used in <c>PutNextEntry()</c> ends in /// a slash, then the entry added is marked as a directory. Because directory /// entries do not contain data, a call to <c>Write()</c>, before an /// intervening additional call to <c>PutNextEntry()</c>, will throw an /// exception. /// </para> /// /// <para> /// If you don't call <c>Write()</c> between two calls to /// <c>PutNextEntry()</c>, the first entry is inserted into the zip file as a /// file of zero size. This may be what you want. /// </para> /// /// <para> /// Because <c>PutNextEntry()</c> closes out the prior entry, if any, this /// method may throw if there is a problem with the prior entry. /// </para> /// /// <para> /// This method returns the <c>ZipEntry</c>. You can modify public properties /// on the <c>ZipEntry</c>, such as <see cref="ZipEntry.Encryption"/>, <see /// cref="ZipEntry.Password"/>, and so on, until the first call to /// <c>ZipOutputStream.Write()</c>, or until the next call to /// <c>PutNextEntry()</c>. If you modify the <c>ZipEntry</c> <em>after</em> /// having called <c>Write()</c>, you may get a runtime exception, or you may /// silently get an invalid zip archive. /// </para> /// /// </remarks> /// /// <example> /// /// This example shows how to create a zip file, using the /// <c>ZipOutputStream</c> class. /// /// <code> /// private void Zipup() /// { /// using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite )) /// { /// using (var output= new ZipOutputStream(fs)) /// { /// output.Password = "******"; /// output.Encryption = EncryptionAlgorithm.WinZipAes256; /// output.PutNextEntry("entry1.txt"); /// byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1."); /// output.Write(buffer,0,buffer.Length); /// output.PutNextEntry("entry2.txt"); // this will be zero length /// output.PutNextEntry("entry3.txt"); /// buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3."); /// output.Write(buffer,0,buffer.Length); /// } /// } /// } /// </code> /// </example> /// /// <param name="entryName"> /// The name of the entry to be added, including any path to be used /// within the zip file. /// </param> /// /// <returns> /// The ZipEntry created. /// </returns> /// public ZipEntry PutNextEntry(String entryName) { if (_disposed) { _exceptionPending = true; throw new System.InvalidOperationException("The stream has been closed."); } _FinishCurrentEntry(); _currentEntry = ZipEntry.CreateForZipOutputStream(entryName); _currentEntry._container = new ZipContainer(this); _currentEntry._BitField |= 0x0008; // workitem 8932 _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now); _currentEntry.CompressionLevel = CompressionLevel; _currentEntry.Encryption = Encryption; _currentEntry.Password = _password; if (entryName.EndsWith("/")) _currentEntry.MarkAsDirectory(); _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0); _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0); InsureUniqueEntry(_currentEntry); _needToWriteEntryHeader = true; return _currentEntry; }
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; // change for workitem 8098 ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream); bytesRead += 4; // Return false if this is not a local file header signature. if (ZipEntry.IsNotValidSig(signature)) { // Getting "not a ZipEntry signature" is not always wrong or an error. // This will happen after the last entry in a zipfile. In that case, we // expect to read : // a ZipDirEntry signature (if a non-empty zip file) or // a ZipConstants.EndOfCentralDirectorySignature. // // Anything else is a surprise. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); } return(false); } byte[] block = new byte[26]; int n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != block.Length) { return(false); } bytesRead += n; int i = 0; ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); ze._BitField = (Int16)(block[i++] + block[i++] * 256); ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; // transform the time data into something usable (a DateTime) ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); ze._timestamp |= ZipEntryTimestamp.DOS; if ((ze._BitField & 0x01) == 0x01) { ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field ze._sourceIsEncrypted = true; } // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and // CRC values are not true values; the true values will follow the entry data. // But, regardless of the status of bit 3 in the bitfield, the slots for // the three amigos may contain marker values for ZIP64. So we must read them. { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); if ((uint)ze._CompressedSize == 0xFFFFFFFF || (uint)ze._UncompressedSize == 0xFFFFFFFF) { ze._InputUsesZip64 = true; } } Int16 filenameLength = (short)(block[i++] + block[i++] * 256); Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); block = new byte[filenameLength]; n = ze.ArchiveStream.Read(block, 0, block.Length); bytesRead += n; // if the UTF8 bit is set for this entry, override the // encoding the application requested. if ((ze._BitField & 0x0800) == 0x0800) { // workitem 12744 ze.AlternateEncoding = System.Text.Encoding.UTF8; ze.AlternateEncodingUsage = ZipOption.Always; } ze._FileNameInArchive = ze.AlternateEncoding.GetString(block); // workitem 6898 if (ze._FileNameInArchive.EndsWith("/")) { ze.MarkAsDirectory(); } bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); ze._LengthOfTrailer = 0; // workitem 6607 - don't read for directories // actually get the compressed size and CRC if necessary if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) { // workitem 8098: ok (restore) long posn = ze.ArchiveStream.Position; // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and // a consistent data record after that. To be consistent, the data record must // indicate the length of the entry data. bool wantMore = true; long SizeOfDataRead = 0; int tries = 0; while (wantMore) { tries++; // We call the FindSignature shared routine to find the specified signature // in the already-opened zip archive, starting from the current cursor // position in that filestream. If we cannot find the signature, then the // routine returns -1, and the ReadHeader() method returns false, // indicating we cannot read a legal entry header. If we have found it, // then the FindSignature() method returns the number of bytes in the // stream we had to seek forward, to find the sig. We need this to // determine if the zip entry is valid, later. if (ze._container.ZipFile != null) { ze._container.ZipFile.OnReadBytes(ze); } long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) { return(false); } // total size of data read (through all loops of this). SizeOfDataRead += d; if (ze._InputUsesZip64) { // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) block = new byte[20]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 20) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._UncompressedSize = BitConverter.ToInt64(block, i); i += 8; } else { // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) block = new byte[12]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 12) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); } wantMore = (SizeOfDataRead != ze._CompressedSize); if (wantMore) { // Seek back to un-read the last 12 bytes - maybe THEY contain // the ZipEntryDataDescriptorSignature. // (12 bytes for the CRC, Comp and Uncomp size.) ze.ArchiveStream.Seek(-12, SeekOrigin.Current); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } // seek back to previous position, to prepare to read file data // workitem 8098: ok (restore) ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); ze._LengthOfTrailer += ze._InputUsesZip64 ? 24 : 16; // bytes including sig, CRC, Comp and Uncomp sizes } ze._CompressedFileDataSize = ze._CompressedSize; // bit 0 set indicates that some kind of encryption is in use if ((ze._BitField & 0x01) == 0x01) { { // read in the header data for "weak" encryption ze._WeakEncryptionHeader = new byte[12]; bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); // decrease the filedata size by 12 bytes ze._CompressedFileDataSize -= 12; } } // Remember the size of the blob for this entry. // We also have the starting position in the stream for this entry. ze._LengthOfHeader = bytesRead; ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; // We've read in the regular entry header, the extra field, and any // encryption header. The pointer in the file is now at the start of the // filedata, which is potentially compressed and encrypted. Just ahead in // the file, there are _CompressedFileDataSize bytes of data, followed by // potentially a non-zero length trailer, consisting of optionally, some // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24 // bytes). return(true); }
/// <summary> /// Reads one entry from the zip directory structure in the zip file. /// </summary> /// <param name="zf"> /// The zipfile for which a directory entry will be read. From this param, the /// method gets the ReadStream and the expected text encoding /// (ProvisionalAlternateEncoding) which is used if the entry is not marked /// UTF-8. /// </param> /// <returns>the entry read from the archive.</returns> internal static ZipEntry ReadDirEntry(ZipFile zf) { System.IO.Stream s = zf.ReadStream; System.Text.Encoding expectedEncoding = zf.ProvisionalAlternateEncoding; int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); // return null if this is not a local file header signature if (IsNotValidZipDirEntrySig(signature)) { s.Seek(-4, System.IO.SeekOrigin.Current); // Getting "not a ZipDirEntry signature" here is not always wrong or an // error. This can happen when walking through a zipfile. After the // last ZipDirEntry, we expect to read an // EndOfCentralDirectorySignature. When we get this is how we know // we've reached the end of the central directory. if (signature != ZipConstants.EndOfCentralDirectorySignature && signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && signature != ZipConstants.ZipEntrySignature // workitem 8299 ) { throw new BadReadException(String.Format(" ZipEntry::ReadDirEntry(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); } return(null); } int bytesRead = 42 + 4; byte[] block = new byte[42]; int n = s.Read(block, 0, block.Length); if (n != block.Length) { return(null); } int i = 0; ZipEntry zde = new ZipEntry(); zde._Source = ZipEntrySource.ZipFile; zde._container = new ZipContainer(zf); unchecked { zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); zde._BitField = (short)(block[i++] + block[i++] * 256); zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); zde._timestamp |= ZipEntryTimestamp.DOS; zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); } // preserve zde._CompressionMethod_FromZipFile = zde._CompressionMethod; zde._filenameLength = (short)(block[i++] + block[i++] * 256); zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); zde._commentLength = (short)(block[i++] + block[i++] * 256); zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); // workitem 7801 zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); block = new byte[zde._filenameLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } // Console.WriteLine("\nEntry : {0}", zde._LocalFileName); // Console.WriteLine(" V Madeby/Needed: 0x{0:X4} / 0x{1:X4}", zde._VersionMadeBy, zde._VersionNeeded); // Console.WriteLine(" BitField/Compression: 0x{0:X4} / 0x{1:X4}", zde._BitField, zde._CompressionMethod); // Console.WriteLine(" Lastmod: {0}", zde._LastModified.ToString("u")); // Console.WriteLine(" CRC: 0x{0:X8}", zde._Crc32); // Console.WriteLine(" Comp / Uncomp: 0x{0:X8} ({0}) 0x{1:X8} ({1})", zde._CompressedSize, zde._UncompressedSize); //zde._FileNameInArchive = zde._LocalFileName; if (zde.AttributesIndicateDirectory) { zde.MarkAsDirectory(); // may append a slash to filename if nec. } // workitem 6898 else if (zde._FileNameInArchive.EndsWith("/")) { zde.MarkAsDirectory(); } zde._CompressedFileDataSize = zde._CompressedSize; if ((zde._BitField & 0x01) == 0x01) { zde._Encryption_FromZipFile = zde._Encryption = EncryptionAlgorithm.PkzipWeak; // this may change after processing the Extra field zde._sourceIsEncrypted = true; } if (zde._extraFieldLength > 0) { zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || zde._UncompressedSize == 0xFFFFFFFF || zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64); bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); zde._CompressedFileDataSize = zde._CompressedSize; } // we've processed the extra field, so we know the encryption method is set now. if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) { // the "encryption header" of 12 bytes precedes the file data zde._CompressedFileDataSize -= 12; } #if AESCRYPTO else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || zde.Encryption == EncryptionAlgorithm.WinZipAes256) { zde._CompressedFileDataSize = zde.CompressedSize - (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10); zde._LengthOfTrailer = 10; } #endif // tally the trailing descriptor if ((zde._BitField & 0x0008) == 0x0008) { // sig, CRC, Comp and Uncomp sizes if (zde._InputUsesZip64) { zde._LengthOfTrailer += 24; } else { zde._LengthOfTrailer += 16; } } if (zde._commentLength > 0) { block = new byte[zde._commentLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); } else { zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); } } //zde._LengthOfDirEntry = bytesRead; return(zde); }
private ZipEntry AddOrUpdateDirectoryImpl(string directoryName, string rootDirectoryPathInArchive, AddOrUpdateAction action, bool recurse, int level) { if (Verbose) { StatusMessageTextWriter.WriteLine("{0} {1}...", (action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating", directoryName); } if (level == 0) { _addOperationCanceled = false; OnAddStarted(); } // workitem 13371 if (_addOperationCanceled) { return(null); } string dirForEntries = rootDirectoryPathInArchive; ZipEntry baseDir = null; if (level > 0) { int f = directoryName.Length; for (int i = level; i > 0; i--) { f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1); } dirForEntries = directoryName.Substring(f + 1); dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries); } // if not top level, or if the root is non-empty, then explicitly add the directory if (level > 0 || rootDirectoryPathInArchive != "") { baseDir = ZipEntry.CreateFromFile(directoryName, dirForEntries); baseDir._container = new ZipContainer(this); baseDir.AlternateEncoding = this.AlternateEncoding; // workitem 6410 baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage; baseDir.MarkAsDirectory(); baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes; baseDir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes; // add the directory only if it does not exist. // It's not an error if it already exists. if (!_entries.ContainsKey(baseDir.FileName)) { InternalAddEntry(baseDir.FileName, baseDir); AfterAddEntry(baseDir); } dirForEntries = baseDir.FileName; } if (!_addOperationCanceled) { String[] filenames = Directory.GetFiles(directoryName); if (recurse) { // add the files: foreach (String filename in filenames) { if (_addOperationCanceled) { break; } if (action == AddOrUpdateAction.AddOnly) { AddFile(filename, dirForEntries); } else { UpdateFile(filename, dirForEntries); } } if (!_addOperationCanceled) { // add the subdirectories: String[] dirnames = Directory.GetDirectories(directoryName); foreach (String dir in dirnames) { // workitem 8617: Optionally traverse reparse points FileAttributes fileAttrs = System.IO.File.GetAttributes(dir); if (this.AddDirectoryWillTraverseReparsePoints || ((fileAttrs & FileAttributes.ReparsePoint) == 0) ) { AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1); } } } } } if (level == 0) { OnAddCompleted(); } return(baseDir); }
/// <summary> /// Reads one entry from the zip directory structure in the zip file. /// </summary> /// <param name="s">the stream from which to read.</param> /// <param name="expectedEncoding"> /// The text encoding to use if the entry is not marked UTF-8. /// </param> /// <returns>the entry read from the archive.</returns> public static ZipEntry ReadDirEntry(System.IO.Stream s, System.Text.Encoding expectedEncoding) { long cdrPosition = s.Position; int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); // return null if this is not a local file header signature if (IsNotValidZipDirEntrySig(signature)) { s.Seek(-4, System.IO.SeekOrigin.Current); // Getting "not a ZipDirEntry signature" here is not always wrong or an error. // This can happen when walking through a zipfile. After the last ZipDirEntry, // we expect to read an EndOfCentralDirectorySignature. When we get this is how we // know we've reached the end of the central directory. if (signature != ZipConstants.EndOfCentralDirectorySignature && signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature) { throw new BadReadException(String.Format(" ZipEntry::ReadDirEntry(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); } return(null); } int bytesRead = 42 + 4; byte[] block = new byte[42]; int n = s.Read(block, 0, block.Length); if (n != block.Length) { return(null); } int i = 0; ZipEntry zde = new ZipEntry(); zde._archiveStream = s; zde._cdrPosition = cdrPosition; zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); zde._BitField = (short)(block[i++] + block[i++] * 256); zde._CompressionMethod = (short)(block[i++] + block[i++] * 256); zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); //DateTime lastModified = Ionic.Utils.Zip.SharedUtilities.PackedToDateTime(lastModDateTime); //i += 24; zde._filenameLength = (short)(block[i++] + block[i++] * 256); zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); zde._commentLength = (short)(block[i++] + block[i++] * 256); //Int16 diskNumber = (short)(block[i++] + block[i++] * 256); i += 2; zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); block = new byte[zde._filenameLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._LocalFileName = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block, block.Length); } else { zde._LocalFileName = Ionic.Zip.SharedUtilities.StringFromBuffer(block, block.Length, expectedEncoding); } // Console.WriteLine("\nEntry : {0}", zde._LocalFileName); // Console.WriteLine(" V Madeby: 0x{0:X4}", zde._VersionMadeBy); // Console.WriteLine(" V Needed: 0x{0:X4}", zde._VersionNeeded); // Console.WriteLine(" BitField: 0x{0:X4}", zde._BitField); // Console.WriteLine(" Compression: 0x{0:X4}", zde._CompressionMethod); // Console.WriteLine(" Lastmod: {0}", zde._LastModified.ToString("u")); // Console.WriteLine(" CRC: 0x{0:X8}", zde._Crc32); // Console.WriteLine(" Comp: 0x{0:X8} ({0})", zde._CompressedSize); // Console.WriteLine(" Uncomp: 0x{0:X8} ({0})", zde._UncompressedSize); zde._FileNameInArchive = zde._LocalFileName; if (zde.AttributesIndicateDirectory) { zde.MarkAsDirectory(); // may append a slash to filename if nec. } // workitem 6898 if (zde._LocalFileName.EndsWith("/")) { zde.MarkAsDirectory(); } zde._CompressedFileDataSize = zde._CompressedSize; if ((zde._BitField & 0x01) == 0x01) { zde._Encryption = EncryptionAlgorithm.PkzipWeak; // this may change after processing the Extra field zde._sourceIsEncrypted = true; } if (zde._extraFieldLength > 0) { //Console.WriteLine("ZDE Extra Field length: {0}", zde._extraFieldLength); zde._InputUsesZip64 = ((uint)zde._CompressedSize == 0xFFFFFFFF || (uint)zde._UncompressedSize == 0xFFFFFFFF || (uint)zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); bytesRead += zde.ProcessExtraField(zde._extraFieldLength); zde._CompressedFileDataSize = zde._CompressedSize; //Console.WriteLine(" Compressed: 0x{0:X8} ({0})", zde._CompressedSize); } // we've processed the extra field, so we know the encryption method is set now. if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) { // the "encryption header" of 12 bytes precedes the file data zde._CompressedFileDataSize -= 12; } #if !NETCF20 else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || zde.Encryption == EncryptionAlgorithm.WinZipAes256) { zde._CompressedFileDataSize = zde.CompressedSize - ((zde._KeyStrengthInBits / 8 / 2) + 10 + 2);// zde._aesCrypto.SizeOfEncryptionMetadata; zde._LengthOfTrailer = 10; //Console.WriteLine(" CFDS: 0x{0:X8} ({0})", zde._CompressedFileDataSize); //Console.WriteLine(" Actual Compression: 0x{0:X4}", zde._CompressionMethod); } #endif if (zde._commentLength > 0) { block = new byte[zde._commentLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block, block.Length); } else { zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, block.Length, expectedEncoding); } } zde._LengthOfDirEntry = bytesRead; return(zde); }
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; // change for workitem 8098 // ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; ze._RelativeOffsetOfLocalHeader = ze._zipfile.RelativeOffset; int signature = Ionic.Zip.SharedUtilities.ReadSignature(ze.ArchiveStream); bytesRead += 4; // Return false if this is not a local file header signature. if (ZipEntry.IsNotValidSig(signature)) { // Getting "not a ZipEntry signature" is not always wrong or an error. // This will happen after the last entry in a zipfile. In that case, we // expect to read : // a ZipDirEntry signature (if a non-empty zip file) or // a ZipConstants.EndOfCentralDirectorySignature. // // Anything else is a surprise. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(" ZipEntry::ReadHeader(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); } return(false); } byte[] block = new byte[26]; int n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != block.Length) { return(false); } bytesRead += n; int i = 0; ze._VersionNeeded = (short)(block[i++] + block[i++] * 256); ze._BitField = (short)(block[i++] + block[i++] * 256); ze._CompressionMethod = (short)(block[i++] + block[i++] * 256); ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; // transform the time data into something usable (a DateTime) ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); ze._timestamp |= ZipEntryTimestamp.DOS; // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and // CRC values are not true values; the true values will follow the entry data. // But, regardless of the status of bit 3 in the bitfield, the slots for // the three amigos may contain marker values for ZIP64. So we must read them. { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); if ((uint)ze._CompressedSize == 0xFFFFFFFF || (uint)ze._UncompressedSize == 0xFFFFFFFF) { ze._InputUsesZip64 = true; } } Int16 filenameLength = (short)(block[i++] + block[i++] * 256); Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); block = new byte[filenameLength]; n = ze.ArchiveStream.Read(block, 0, block.Length); bytesRead += n; // if the UTF8 bit is set for this entry, override the encoding the application requested. ze._actualEncoding = ((ze._BitField & 0x0800) == 0x0800) ? System.Text.Encoding.UTF8 : defaultEncoding; // need to use this form of GetString() for .NET CF ze._FileNameInArchive = ze._actualEncoding.GetString(block, 0, block.Length); // when creating an entry by reading, the LocalFileName is the same as the FileNameInArchive ze._LocalFileName = ze._FileNameInArchive; // workitem 6898 if (ze._LocalFileName.EndsWith("/")) { ze.MarkAsDirectory(); } bytesRead += ze.ProcessExtraField(extraFieldLength); ze._LengthOfTrailer = 0; // workitem 6607 - don't read for directories // actually get the compressed size and CRC if necessary if (!ze._LocalFileName.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) { // This descriptor exists only if bit 3 of the general // purpose bit flag is set (see below). It is byte aligned // and immediately follows the last byte of compressed data, // as well as any encryption trailer, as with AES. // This descriptor is used only when it was not possible to // seek in the output .ZIP file, e.g., when the output .ZIP file // was standard output or a non-seekable device. For ZIP64(tm) format // archives, the compressed and uncompressed sizes are 8 bytes each. // workitem 8098: ok (restore) long posn = ze.ArchiveStream.Position; // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and // a consistent data record after that. To be consistent, the data record must // indicate the length of the entry data. bool wantMore = true; long SizeOfDataRead = 0; int tries = 0; while (wantMore) { tries++; // We call the FindSignature shared routine to find the specified signature // in the already-opened zip archive, starting from the current cursor // position in that filestream. If we cannot find the signature, then the // routine returns -1, and the ReadHeader() method returns false, // indicating we cannot read a legal entry header. If we have found it, // then the FindSignature() method returns the number of bytes in the // stream we had to seek forward, to find the sig. We need this to // determine if the zip entry is valid, later. ze._zipfile.OnReadBytes(ze); long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) { return(false); } // total size of data read (through all loops of this). SizeOfDataRead += d; if (ze._InputUsesZip64) { // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) block = new byte[20]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 20) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._UncompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes } else { // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) block = new byte[12]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 12) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes } wantMore = (SizeOfDataRead != ze._CompressedSize); if (wantMore) { // Seek back to un-read the last 12 bytes - maybe THEY contain // the ZipEntryDataDescriptorSignature. // (12 bytes for the CRC, Comp and Uncomp size.) ze.ArchiveStream.Seek(-12, SeekOrigin.Current); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } // seek back to previous position, to prepare to read file data // workitem 8098: ok (restore) ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); } ze._CompressedFileDataSize = ze._CompressedSize; // bit 0 set indicates that some kind of encryption is in use if ((ze._BitField & 0x01) == 0x01) { #if AESCRYPTO if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || ze.Encryption == EncryptionAlgorithm.WinZipAes256) { // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. ze._aesCrypto = WinZipAesCrypto.ReadFromStream(null, ze._KeyStrengthInBits, ze.ArchiveStream); bytesRead += ze._aesCrypto.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) // according to WinZip, the CompressedSize includes the AES Crypto framing data. ze._CompressedFileDataSize -= ze._aesCrypto.SizeOfEncryptionMetadata; ze._LengthOfTrailer += 10; // MAC } else #endif { // read in the header data for "weak" encryption ze._WeakEncryptionHeader = new byte[12]; bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); // decrease the filedata size by 12 bytes ze._CompressedFileDataSize -= 12; } } // Remember the size of the blob for this entry. // We also have the starting position in the stream for this entry. ze._LengthOfHeader = bytesRead; ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; // We've read in the regular entry header, the extra field, and any encryption // header. The pointer in the file is now at the start of the filedata, which is // potentially compressed and encrypted. Just ahead in the file, there are // _CompressedFileDataSize bytes of data, followed by potentially a non-zero length // trailer, consisting of optionally, some encryption stuff (10 byte MAC for AES), // and the bit-3 trailer (16 or 24 bytes). return(true); }