void ExtractEntry(ZipEntry entry) { bool doExtraction = false; string nameText = entry.Name; if (entry.IsFile) { // TODO: Translate invalid names allowing extraction still. doExtraction = NameIsValid(nameText) && entry.IsCompressionMethodSupported(); } else if (entry.IsDirectory) { doExtraction = NameIsValid(nameText); } // TODO: Fire delegate were compression method not supported, or name is invalid? string dirName = null; string targetName = null; if (doExtraction) { // Handle invalid entry names by chopping of path root. if (Path.IsPathRooted(nameText)) { string workName = Path.GetPathRoot(nameText); nameText = nameText.Substring(workName.Length); } if (nameText.Length > 0) { targetName = Path.Combine(targetDirectory_, nameText); if (entry.IsDirectory) { dirName = targetName; } else { dirName = Path.GetDirectoryName(Path.GetFullPath(targetName)); } } else { doExtraction = false; } } if (doExtraction && !Directory.Exists(dirName)) { if (!entry.IsDirectory || CreateEmptyDirectories) { try { Directory.CreateDirectory(dirName); } catch (Exception ex) { doExtraction = false; if (events_ != null) { if (entry.IsDirectory) { continueRunning_ = events_.OnDirectoryFailure(targetName, ex); } else { continueRunning_ = events_.OnFileFailure(targetName, ex); } } else { continueRunning_ = false; } } } } if (doExtraction && entry.IsFile) { ExtractFileEntry(entry, targetName); } }
/// <summary> /// Make a new <see cref="ZipEntry"></see> for a directory. /// </summary> /// <param name="directoryName">The raw untransformed name for the new directory</param> /// <param name="useFileSystem">If true entry detail is retrieved from the file system if the file exists.</param> /// <returns>Returns a new <see cref="ZipEntry"></see> representing a directory.</returns> public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem) { ZipEntry result = new ZipEntry(nameTransform_.TransformDirectory(directoryName)); result.Size=0; int externalAttributes = 0; DirectoryInfo di = null; if (useFileSystem) { di = new DirectoryInfo(directoryName); } if ((di != null) && di.Exists) { switch (timeSetting_) { case TimeSetting.CreateTime: result.DateTime = di.CreationTime; break; case TimeSetting.CreateTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = di.CreationTime.ToUniversalTime(); #else result.DateTime = di.CreationTimeUtc; #endif break; case TimeSetting.LastAccessTime: result.DateTime = di.LastAccessTime; break; case TimeSetting.LastAccessTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = di.LastAccessTime.ToUniversalTime(); #else result.DateTime = di.LastAccessTimeUtc; #endif break; case TimeSetting.LastWriteTime: result.DateTime = di.LastWriteTime; break; case TimeSetting.LastWriteTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = di.LastWriteTime.ToUniversalTime(); #else result.DateTime = di.LastWriteTimeUtc; #endif break; case TimeSetting.Fixed: result.DateTime = fixedDateTime_; break; default: throw new ZipException("Unhandled time setting in MakeDirectoryEntry"); } externalAttributes = ((int)di.Attributes & getAttributes_); } else { if (timeSetting_ == TimeSetting.Fixed) { result.DateTime = fixedDateTime_; } } // Always set directory attribute on. externalAttributes |= (setAttributes_ | 16); result.ExternalFileAttributes = externalAttributes; return result; }
/// <summary> /// Gets an input stream for reading the given zip entry data in an uncompressed form. /// Normally the <see cref="ZipEntry"/> should be an entry returned by GetEntry(). /// </summary> /// <param name="entry">The <see cref="ZipEntry"/> to obtain a data <see cref="Stream"/> for</param> /// <returns>An input <see cref="Stream"/> containing data for this <see cref="ZipEntry"/></returns> /// <exception cref="InvalidOperationException"> /// The ZipFile has already been closed /// </exception> /// <exception cref="ICCEmbedded.SharpZipLib.Zip.ZipException"> /// The compression method for the entry is unknown /// </exception> /// <exception cref="IndexOutOfRangeException"> /// The entry is not found in the ZipFile /// </exception> public Stream GetInputStream(ZipEntry entry) { if ( entry == null ) { throw new ArgumentNullException("entry"); } if ( entries_ == null ) { throw new InvalidOperationException("ZipFile has closed"); } long index = entry.ZipFileIndex; if ( (index < 0) || (index >= entries_.Length) || (entries_[index].Name != entry.Name) ) { index = FindEntry(entry.Name, true); if (index < 0) { throw new ZipException("Entry cannot be found"); } } return GetInputStream(index); }
public ZipEntry(ZipEntry entry) { if ( entry == null ) { throw new ArgumentNullException("entry"); } known = entry.known; name = entry.name; size = entry.size; compressedSize = entry.compressedSize; crc = entry.crc; dosTime = entry.dosTime; method = entry.method; comment = entry.comment; versionToExtract = entry.versionToExtract; versionMadeBy = entry.versionMadeBy; externalFileAttributes = entry.externalFileAttributes; flags = entry.flags; zipFileIndex = entry.zipFileIndex; offset = entry.offset; forceZip64_ = entry.forceZip64_; if ( entry.extra != null ) { extra = new byte[entry.extra.Length]; Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length); } }
public ZipEntryEnumerator(ZipEntry[] entries) { array = entries; }
/// <summary> /// Opens a Zip file reading the given <see cref="Stream"/>. /// </summary> /// <param name="stream">The <see cref="Stream"/> to read archive data from.</param> /// <exception cref="IOException"> /// An i/o error occurs /// </exception> /// <exception cref="ZipException"> /// The file doesn't contain a valid zip archive.<br/> /// The stream provided cannot seek /// </exception> public ZipFile(Stream stream) { if ( stream == null ) { throw new ArgumentNullException("stream"); } if ( !stream.CanSeek ) { throw new ArgumentException("Stream is not seekable", "stream"); } baseStream_ = stream; isStreamOwner = true; if ( baseStream_.Length > 0 ) { try { ReadEntries(); } catch { DisposeInternal(true); throw; } } else { entries_ = new ZipEntry[0]; isNewArchive_ = true; } }
public ZipUpdate(string fileName, ZipEntry entry) { command_ = UpdateCommand.Add; entry_ = entry; filename_ = fileName; }
Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry) { CryptoStream result = null; if ( (entry.Version < ZipConstants.VersionStrongEncryption) || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) { PkzipClassicManaged classicManaged = new PkzipClassicManaged(); OnKeysRequired(entry.Name); if (HaveKeys == false) { throw new ZipException("No password available for encrypted stream"); } // Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream // which doesnt do this. result = new CryptoStream(new UncompressedStream(baseStream), classicManaged.CreateEncryptor(key, null), CryptoStreamMode.Write); if ( (entry.Crc < 0) || (entry.Flags & 8) != 0) { WriteEncryptionHeader(result, entry.DosTime << 16); } else { WriteEncryptionHeader(result, entry.Crc); } } return result; }
/// <summary> /// Get an output stream for the specified <see cref="ZipEntry"/> /// </summary> /// <param name="entry">The entry to get an output stream for.</param> /// <returns>The output stream obtained for the entry.</returns> Stream GetOutputStream(ZipEntry entry) { Stream result = baseStream_; if ( entry.IsCrypted == true ) { #if NETCF_1_0 throw new ZipException("Encryption not supported for Compact Framework 1.0"); #else result = CreateAndInitEncryptionStream(result, entry); #endif } switch ( entry.CompressionMethod ) { case CompressionMethod.Stored: result = new UncompressedStream(result); break; case CompressionMethod.Deflated: DeflaterOutputStream dos = new DeflaterOutputStream(result, new Deflater(9, true)); dos.IsStreamOwner = false; result = dos; break; default: throw new ZipException("Unknown compression method " + entry.CompressionMethod); } return result; }
internal void SetEntry(ZipEntry entry) { entry_ = entry; entryValid_ = true; bytesTested_ = 0; }
int FindExistingUpdate(ZipEntry entry) { int result = -1; string convertedName = GetTransformedFileName(entry.Name); if (updateIndex_.ContainsKey(convertedName)) { result = (int)updateIndex_[convertedName]; } /* // This is slow like the coming of the next ice age but takes less storage and may be useful // for CF? for (int index = 0; index < updates_.Count; ++index) { ZipUpdate zu = ( ZipUpdate )updates_[index]; if ( (zu.Entry.ZipFileIndex == entry.ZipFileIndex) && (string.Compare(convertedName, zu.Entry.Name, true, CultureInfo.InvariantCulture) == 0) ) { result = index; break; } } */ return result; }
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; }
// 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); } }
/// <summary> /// Write a data descriptor. /// </summary> /// <param name="entry">The entry to write a descriptor for.</param> /// <returns>Returns the number of descriptor bytes written.</returns> public int WriteDataDescriptor(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } int result=0; // Add data descriptor if flagged as required if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) { // The signature is not PKZIP originally but is now described as optional // in the PKZIP Appnote documenting trhe format. WriteLEInt(ZipConstants.DataDescriptorSignature); WriteLEInt(unchecked((int)(entry.Crc))); result+=8; if (entry.LocalHeaderRequiresZip64) { WriteLELong(entry.CompressedSize); WriteLELong(entry.Size); result+=16; } else { WriteLEInt((int)entry.CompressedSize); WriteLEInt((int)entry.Size); result+=8; } } return result; }
public ZipUpdate(string fileName, string entryName, CompressionMethod compressionMethod) { command_ = UpdateCommand.Add; entry_ = new ZipEntry(entryName); entry_.CompressionMethod = compressionMethod; filename_ = fileName; }
public ZipUpdate(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) { command_ = UpdateCommand.Add; entry_ = new ZipEntry(entryName); entry_.CompressionMethod = compressionMethod; dataSource_ = dataSource; }
static void CheckClassicPassword(CryptoStream classicCryptoStream, ZipEntry entry) { byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; StreamUtils.ReadFully(classicCryptoStream, cryptbuffer); if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) { throw new ZipException("Invalid password"); } }
public ZipUpdate(IStaticDataSource dataSource, ZipEntry entry) { command_ = UpdateCommand.Add; entry_ = entry; dataSource_ = dataSource; }
/// <summary> /// Get a <see cref="Stream"/> providing data for an entry. /// </summary> /// <param name="entry">The entry to provide data for.</param> /// <param name="name">The file name for data if known.</param> /// <returns>Returns a stream providing data; or null if not available</returns> public Stream GetSource(ZipEntry entry, string name) { Stream result = null; if ( name != null ) { result = File.OpenRead(name); } return result; }
public ZipUpdate(ZipEntry original, ZipEntry updated) { throw new ZipException("Modify not currently supported"); /* command_ = UpdateCommand.Modify; entry_ = ( ZipEntry )original.Clone(); outEntry_ = ( ZipEntry )updated.Clone(); */ }
/// <summary> /// Initialises a default <see cref="ZipFile"/> instance with no entries and no file storage. /// </summary> internal ZipFile() { entries_ = new ZipEntry[0]; isNewArchive_ = true; }
public ZipUpdate(UpdateCommand command, ZipEntry entry) { command_ = command; entry_ = ( ZipEntry )entry.Clone(); }
/// <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 ed = new ZipExtraData(extraData); // Extra data / zip64 checks if (ed.Find(1)) { // TODO Check for tag values being distinct.. Multiple zip64 tags means what? // Zip64 extra data but 'extract version' is too low if (extractVersion < ZipConstants.VersionZip64) { throw new ZipException( string.Format("Extra data contains Zip64 information but version {0}.{1} is not high enough", extractVersion / 10, extractVersion % 10)); } // Zip64 extra data but size fields dont indicate its required. if (((uint)size != uint.MaxValue) && ((uint)compressedSize != uint.MaxValue)) { throw new ZipException("Entry sizes not correct for Zip64"); } size = ed.ReadLong(); compressedSize = ed.ReadLong(); } 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"); } // 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 size. if ( entry.IsDirectory ) { if ( (compressedSize != 0) || (size != 0) ) { throw new ZipException("Directory cannot have size"); } } 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) { 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; } }
/// <summary> /// Copy an existing entry. /// </summary> /// <param name="entry">The existing entry to copy.</param> public ZipUpdate(ZipEntry entry) : this(UpdateCommand.Copy, entry) { // Do nothing. }
void ExtractFileEntry(ZipEntry entry, string targetName) { bool proceed = true; if (overwrite_ != Overwrite.Always) { if (File.Exists(targetName)) { if ((overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null)) { proceed = confirmDelegate_(targetName); } else { proceed = false; } } } if (proceed) { if (events_ != null) { continueRunning_ = events_.OnProcessFile(entry.Name); } if (continueRunning_) { try { using (FileStream outputStream = File.Create(targetName)) { if (buffer_ == null) { buffer_ = new byte[4096]; } if ((events_ != null) && (events_.Progress != null)) { StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_, events_.Progress, events_.ProgressInterval, this, entry.Name); } else { StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_); } if (events_ != null) { continueRunning_ = events_.OnCompletedFile(entry.Name); } } #if !NETCF_1_0 && !NETCF_2_0 if (restoreDateTimeOnExtract_) { File.SetLastWriteTime(targetName, entry.DateTime); } if (RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1)) { FileAttributes fileAttributes = (FileAttributes)entry.ExternalFileAttributes; // TODO: FastZip - Setting of other file attributes on extraction is a little trickier. fileAttributes &= (FileAttributes.Archive | FileAttributes.Normal | FileAttributes.ReadOnly | FileAttributes.Hidden); File.SetAttributes(targetName, fileAttributes); } #endif } catch (Exception ex) { if (events_ != null) { continueRunning_ = events_.OnFileFailure(targetName, ex); } else { continueRunning_ = false; } } } } }
/// <summary> /// Search for and read the central directory of a zip file filling the entries array. /// </summary> /// <exception cref="System.IO.IOException"> /// An i/o error occurs. /// </exception> /// <exception cref="ICCEmbedded.SharpZipLib.Zip.ZipException"> /// The central directory is malformed or cannot be found /// </exception> void ReadEntries() { // Search for the End Of Central Directory. When a zip comment is // present the directory will start earlier // // The search is limited to 64K which is the maximum size of a trailing comment field to aid speed. // This should be compatible with both SFX and ZIP files but has only been tested for Zip files // If a SFX file has the Zip data attached as a resource and there are other resources occuring later then // this could be invalid. // Could also speed this up by reading memory in larger blocks. if (baseStream_.CanSeek == false) { throw new ZipException("ZipFile stream must be seekable"); } long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); if (locatedEndOfCentralDir < 0) { throw new ZipException("Cannot find central directory"); } // Read end of central directory record ushort thisDiskNumber = ReadLEUshort(); ushort startCentralDirDisk = ReadLEUshort(); ulong entriesForThisDisk = ReadLEUshort(); ulong entriesForWholeCentralDir = ReadLEUshort(); ulong centralDirSize = ReadLEUint(); long offsetOfCentralDir = ReadLEUint(); uint commentSize = ReadLEUshort(); if ( commentSize > 0 ) { byte[] comment = new byte[commentSize]; StreamUtils.ReadFully(baseStream_, comment); comment_ = ZipConstants.ConvertToString(comment); } else { comment_ = string.Empty; } bool isZip64 = false; // Check if zip64 header information is required. if ( (thisDiskNumber == 0xffff) || (startCentralDirDisk == 0xffff) || (entriesForThisDisk == 0xffff) || (entriesForWholeCentralDir == 0xffff) || (centralDirSize == 0xffffffff) || (offsetOfCentralDir == 0xffffffff) ) { isZip64 = true; long offset = LocateBlockWithSignature(ZipConstants.Zip64CentralDirLocatorSignature, locatedEndOfCentralDir, 0, 0x1000); if ( offset < 0 ) { throw new ZipException("Cannot find Zip64 locator"); } // number of the disk with the start of the zip64 end of central directory 4 bytes // relative offset of the zip64 end of central directory record 8 bytes // total number of disks 4 bytes ReadLEUint(); // startDisk64 is not currently used ulong offset64 = ReadLEUlong(); uint totalDisks = ReadLEUint(); baseStream_.Position = (long)offset64; long sig64 = ReadLEUint(); if ( sig64 != ZipConstants.Zip64CentralFileHeaderSignature ) { throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64)); } // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12. ulong recordSize = ( ulong )ReadLEUlong(); int versionMadeBy = ReadLEUshort(); int versionToExtract = ReadLEUshort(); uint thisDisk = ReadLEUint(); uint centralDirDisk = ReadLEUint(); entriesForThisDisk = ReadLEUlong(); entriesForWholeCentralDir = ReadLEUlong(); centralDirSize = ReadLEUlong(); offsetOfCentralDir = (long)ReadLEUlong(); // NOTE: zip64 extensible data sector (variable size) is ignored. } entries_ = new ZipEntry[entriesForThisDisk]; // SFX/embedded support, find the offset of the first entry vis the start of the stream // This applies to Zip files that are appended to the end of an SFX stub. // Or are appended as a resource to an executable. // Zip files created by some archivers have the offsets altered to reflect the true offsets // and so dont require any adjustment here... // TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths? if ( !isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize)) ) { offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir); if (offsetOfFirstEntry <= 0) { throw new ZipException("Invalid embedded zip archive"); } } baseStream_.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin); for (ulong i = 0; i < entriesForThisDisk; i++) { if (ReadLEUint() != ZipConstants.CentralHeaderSignature) { throw new ZipException("Wrong Central Directory signature"); } int versionMadeBy = ReadLEUshort(); int versionToExtract = ReadLEUshort(); int bitFlags = ReadLEUshort(); int method = ReadLEUshort(); uint dostime = ReadLEUint(); uint crc = ReadLEUint(); long csize = (long)ReadLEUint(); long size = (long)ReadLEUint(); int nameLen = ReadLEUshort(); int extraLen = ReadLEUshort(); int commentLen = ReadLEUshort(); int diskStartNo = ReadLEUshort(); // Not currently used int internalAttributes = ReadLEUshort(); // Not currently used uint externalAttributes = ReadLEUint(); long offset = ReadLEUint(); byte[] buffer = new byte[Math.Max(nameLen, commentLen)]; StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen); string name = ZipConstants.ConvertToStringExt(bitFlags, buffer, nameLen); ZipEntry entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method); entry.Crc = crc & 0xffffffffL; entry.Size = size & 0xffffffffL; entry.CompressedSize = csize & 0xffffffffL; entry.Flags = bitFlags; entry.DosTime = (uint)dostime; entry.ZipFileIndex = (long)i; entry.Offset = offset; entry.ExternalFileAttributes = (int)externalAttributes; if ((bitFlags & 8) == 0) { entry.CryptoCheckValue = (byte)(crc >> 24); } else { entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); } if (extraLen > 0) { byte[] extra = new byte[extraLen]; StreamUtils.ReadFully(baseStream_, extra); entry.ExtraData = extra; } entry.ProcessExtraData(false); if (commentLen > 0) { StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen); entry.Comment = ZipConstants.ConvertToStringExt(bitFlags, buffer, commentLen); } entries_[i] = entry; } }
/// <summary> /// Make a new <see cref="ZipEntry"/> from a name. /// </summary> /// <param name="fileName">The name of the file to create a new entry for.</param> /// <param name="useFileSystem">If true entry detail is retrieved from the file system if the file exists.</param> /// <returns>Returns a new <see cref="ZipEntry"/> based on the <paramref name="fileName"/>.</returns> public ZipEntry MakeFileEntry(string fileName, bool useFileSystem) { ZipEntry result = new ZipEntry(nameTransform_.TransformFile(fileName)); result.IsUnicodeText = isUnicodeText_; int externalAttributes = 0; bool useAttributes = (setAttributes_ != 0); FileInfo fi = null; if (useFileSystem) { fi = new FileInfo(fileName); } if ((fi != null) && fi.Exists) { switch (timeSetting_) { case TimeSetting.CreateTime: result.DateTime = fi.CreationTime; break; case TimeSetting.CreateTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = fi.CreationTime.ToUniversalTime(); #else result.DateTime = fi.CreationTimeUtc; #endif break; case TimeSetting.LastAccessTime: result.DateTime = fi.LastAccessTime; break; case TimeSetting.LastAccessTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = fi.LastAccessTime.ToUniversalTime(); #else result.DateTime = fi.LastAccessTimeUtc; #endif break; case TimeSetting.LastWriteTime: result.DateTime = fi.LastWriteTime; break; case TimeSetting.LastWriteTimeUtc: #if NETCF_1_0 || NETCF_2_0 result.DateTime = fi.LastWriteTime.ToUniversalTime(); #else result.DateTime = fi.LastWriteTimeUtc; #endif break; case TimeSetting.Fixed: result.DateTime = fixedDateTime_; break; default: throw new ZipException("Unhandled time setting in MakeFileEntry"); } result.Size = fi.Length; useAttributes = true; externalAttributes = ((int)fi.Attributes & getAttributes_); } else { if (timeSetting_ == TimeSetting.Fixed) { result.DateTime = fixedDateTime_; } } if (useAttributes) { externalAttributes |= setAttributes_; result.ExternalFileAttributes = externalAttributes; } return result; }
/// <summary> /// Locate the data for a given entry. /// </summary> /// <returns> /// The start offset of the data. /// </returns> /// <exception cref="System.IO.EndOfStreamException"> /// The stream ends prematurely /// </exception> /// <exception cref="ICCEmbedded.SharpZipLib.Zip.ZipException"> /// The local header signature is invalid, the entry and central header file name lengths are different /// or the local and entry compression methods dont match /// </exception> long LocateEntry(ZipEntry entry) { return TestLocalHeader(entry, HeaderTest.Extract); }
// 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); } }
Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) { CryptoStream result = null; if ( (entry.Version < ZipConstants.VersionStrongEncryption) || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) { PkzipClassicManaged classicManaged = new PkzipClassicManaged(); OnKeysRequired(entry.Name); if (HaveKeys == false) { throw new ZipException("No password available for encrypted stream"); } result = new CryptoStream(baseStream, classicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read); CheckClassicPassword(result, entry); } else { throw new ZipException("Decryption method not supported"); } return result; }
/// <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 = true; if (method == CompressionMethod.Stored) { // Cant store values in a data descriptor as you cant extract stored files // if the length isnt known. entry.Flags &= ~8; if (entry.CompressedSize >= 0) { if (entry.Size < 0) { entry.Size = entry.CompressedSize; } else if (entry.Size != entry.CompressedSize) { throw new ZipException("Method STORED, but compressed size != size"); } } else { if (entry.Size >= 0) { entry.CompressedSize = entry.Size; } } if (entry.Size < 0 || entry.Crc < 0) { if (CanPatchEntries == true) { headerInfoAvailable = false; } else { // Can't patch entries so storing is not possible. method = CompressionMethod.Deflated; compressionLevel = 0; } } } if (method == CompressionMethod.Deflated) { if (entry.Size == 0) { // No need to compress - no data. entry.CompressedSize = entry.Size; entry.Crc = 0; method = CompressionMethod.Stored; } else if ( (entry.CompressedSize < 0) || (entry.Size < 0) || (entry.Crc < 0) ) { headerInfoAvailable = false; } } if (headerInfoAvailable == false) { if (CanPatchEntries == false) { // Only way to record size and compressed size is to append a data descriptor // after compressed data. 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)method); 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 == true) { 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 && (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 ( patchEntryHeader ) { sizePatchPos = ed.CurrentReadIndex; } } else { ed.Delete(1); } 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; // Activate the entry. curEntry = entry; crc.Reset(); if (method == CompressionMethod.Deflated) { deflater_.Reset(); deflater_.SetLevel(compressionLevel); } size = 0; if (entry.IsCrypted == true) { if (entry.Crc < 0) { // so testing Zip will says its ok WriteEncryptionHeader(entry.DosTime << 16); } else { WriteEncryptionHeader(entry.Crc); } } }