/// <summary> /// Opens a Zip file reading the given <see cref="Stream"/>. /// </summary> /// <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; if ( baseStream_.Length > 0 ) { try { ReadEntries(); } catch { DisposeInternal(true); throw; } } else { entries_ = new ZipEntry[0]; isNewArchive_ = true; } }
/// <summary> /// Creates an input stream reading the given zip entry as /// uncompressed data. Normally zip entry should be an entry /// returned by GetEntry(). /// </summary> /// <returns> /// the input stream. /// </returns> /// <exception cref="InvalidOperationException"> /// The ZipFile has already been closed /// </exception> /// <exception cref="_videoConference.ZipUnzip.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); }
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; }
public ZipEntryEnumerator(ZipEntry[] entries) { array = entries; }
/// <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. }
/// <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="_videoConference.ZipUnzip.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); }
/// <summary> /// Add a <see cref="ZipEntry"/> that contains no data. /// </summary> /// <param name="entry">The entry to add.</param> public void Add(ZipEntry entry) { if ( entry == null ) { throw new ArgumentNullException("entry"); } CheckUpdating(); if ( (entry.Size != 0) || (entry.CompressedSize != 0) ) { throw new ZipException("Entry cannot have any data"); } contentsEdited_ = true; updates_.Add(new ZipUpdate(UpdateCommand.Add, entry)); }
public ZipUpdate(ZipEntry original, ZipEntry updated) { throw new ZipException("Modify not currently supported"); /* command_ = UpdateCommand.Modify; entry_ = ( ZipEntry )original.Clone(); outEntry_ = ( ZipEntry )updated.Clone(); #if FORCE_ZIP64 entry_.ForceZip64(); #endif */ }
/// <summary> /// Closes the current entry, updating header and footer information as required /// </summary> /// <exception cref="System.IO.IOException"> /// An I/O error occurs. /// </exception> /// <exception cref="System.InvalidOperationException"> /// No entry is active. /// </exception> public void CloseEntry() { if (curEntry == null) { throw new InvalidOperationException("No open entry"); } // First finish the deflater, if appropriate if (curMethod == CompressionMethod.Deflated) { base.Finish(); } long csize = curMethod == CompressionMethod.Deflated ? def.TotalOut : size; if (curEntry.Size < 0) { curEntry.Size = size; } else if (curEntry.Size != size) { throw new ZipException("size was " + size + ", but I expected " + curEntry.Size); } if (curEntry.CompressedSize < 0) { curEntry.CompressedSize = csize; } else if (curEntry.CompressedSize != csize) { throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize); } if (curEntry.Crc < 0) { curEntry.Crc = crc.Value; } else if (curEntry.Crc != crc.Value) { throw new ZipException("crc was " + crc.Value + ", but I expected " + curEntry.Crc); } offset += csize; if (curEntry.IsCrypted == true) { curEntry.CompressedSize += ZipConstants.CryptoHeaderSize; } // Patch the header if possible if (patchEntryHeader == true) { patchEntryHeader = false; long curPos = baseOutputStream.Position; baseOutputStream.Seek(crcPatchPos, SeekOrigin.Begin); WriteLeInt((int)curEntry.Crc); if ( curEntry.LocalHeaderRequiresZip64 ) { baseOutputStream.Seek(sizePatchPos, SeekOrigin.Begin); WriteLeLong(curEntry.Size); WriteLeLong(curEntry.CompressedSize); } else { WriteLeInt((int)curEntry.CompressedSize); WriteLeInt((int)curEntry.Size); } baseOutputStream.Seek(curPos, SeekOrigin.Begin); } // Add data descriptor if flagged as required if ((curEntry.Flags & 8) != 0) { WriteLeInt(ZipConstants.DataDescriptorSignature); WriteLeInt((int)curEntry.Crc); if ( curEntry.LocalHeaderRequiresZip64 ) { WriteLeLong(curEntry.CompressedSize); WriteLeLong(curEntry.Size); offset += ZipConstants.Zip64DataDescriptorSize; } else { WriteLeInt((int)curEntry.CompressedSize); WriteLeInt((int)curEntry.Size); offset += ZipConstants.DataDescriptorSize; } } entries.Add(curEntry); curEntry = null; }
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); } }
/// <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; entry.Flags = 0; 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; // Write the local file header WriteLeInt(ZipConstants.LocalHeaderSignature); // This is safe/conservative but can enlarge archives slightly when not needed. // Perhaps implement something to allow a choice here? #if !FORCE_ZIP64 if ( entry.Size < 0 ) #endif { entry.ForceZip64(); } 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 ) { WriteLeInt(-1); WriteLeInt(-1); } else { WriteLeInt(0); // Compressed size WriteLeInt(0); // Uncompressed size } } byte[] name = ZipConstants.ConvertToArray(entry.Name); if (name.Length > 0xFFFF) { throw new ZipException("Entry name too long."); } ZipExtraData ed = new ZipExtraData(entry.ExtraData); if ( entry.LocalHeaderRequiresZip64 ) { ed.StartNewEntry(); 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) { def.Reset(); def.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); } } }
void ExtractEntry(ZipEntry entry) { bool doExtraction = NameIsValid(entry.Name) && entry.IsCompressionMethodSupported(); // TODO: Fire delegate were compression method not supported. string dirName = null; string targetName = null; if ( doExtraction ) { string entryFileName; if (Path.IsPathRooted(entry.Name)) { string workName = Path.GetPathRoot(entry.Name); workName = entry.Name.Substring(workName.Length); entryFileName = Path.Combine(Path.GetDirectoryName(workName), Path.GetFileName(entry.Name)); } else { entryFileName = entry.Name; } targetName = Path.Combine(targetDirectory_, entryFileName); dirName = Path.GetDirectoryName(Path.GetFullPath(targetName)); doExtraction = (entryFileName.Length > 0); } if ( doExtraction && !Directory.Exists(dirName) ) { if ( !entry.IsDirectory || CreateEmptyDirectories ) { try { Directory.CreateDirectory(dirName); } catch { doExtraction = false; } } } if ( doExtraction && entry.IsFile ) { ExtractFileEntry(entry, targetName); } }
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]; } StreamUtils.Copy(inputStream_, outputStream, buffer_); } if ( restoreDateTimeOnExtract_ ) { File.SetLastWriteTime(targetName, entry.DateTime); } } catch(Exception ex) { if ( events_ != null ) { continueRunning_ = events_.OnFileFailure(targetName, ex); } else { continueRunning_ = false; } } } } }
void ProcessFile(object sender, ScanEventArgs e) { if ( (events_ != null) && (events_.ProcessFile != null) ) { events_.ProcessFile(sender, e); } if ( e.ContinueRunning ) { string cleanedName = nameTransform_.TransformFile(e.Name); ZipEntry entry = new ZipEntry(cleanedName); FileInfo info = new FileInfo(e.Name); UpdateEntry(entry, info); outputStream_.PutNextEntry(entry); AddFileContents(e.Name); } }
/// <summary> /// Add a directory entry to the archive. /// </summary> /// <param name="directoryName">The directory to add.</param> public void AddDirectory(string directoryName) { if ( directoryName == null ) { throw new ArgumentNullException("directoryName"); } ZipEntry dirEntry = new ZipEntry(GetTransformedDirectoryName(directoryName)); dirEntry.ExternalFileAttributes = 16; updates_.Add(new ZipUpdate(UpdateCommand.Add, dirEntry)); }
public ZipUpdate(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) { command_ = UpdateCommand.Add; entry_ = new ZipEntry(entryName); entry_.CompressionMethod = compressionMethod; #if FORCE_ZIP64 entry_.ForceZip64(); #endif dataSource_ = dataSource; }
/// <summary> /// Delete a <see cref="ZipEntry"/> from the archive. /// </summary> /// <param name="entry">The entry to delete.</param> public void Delete(ZipEntry entry) { CheckUpdating(); int index = FindExistingUpdate(entry); if ( index >= 0 ) { contentsEdited_ = true; updates_.RemoveAt(index); } else { throw new ZipException("Cannot find entry to delete"); } }
public ZipUpdate(UpdateCommand command, ZipEntry entry) { command_ = command; entry_ = ( ZipEntry )entry.Clone(); #if FORCE_ZIP64 entry_.ForceZip64(); #endif }
int WriteCentralDirectoryHeader(ZipEntry entry) { // Write the central file header WriteLEInt(ZipConstants.CentralHeaderSignature); // Version made by WriteLEShort(ZipConstants.VersionMadeBy); // Version required to extract WriteLEShort(entry.Version); WriteLEShort(entry.Flags); WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); if ( entry.CompressedSize >= 0xffffffff ) { WriteLEInt(-1); } else { WriteLEInt((int)(entry.CompressedSize & 0xffffffff)); } if ( entry.Size >= 0xffffffff ) { WriteLEInt(-1); } else { WriteLEInt((int)entry.Size); } byte[] name = ZipConstants.ConvertToArray(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 !FORCE_ZIP64 if ( entry.Size >= 0xffffffff ) #endif { ed.AddLeLong(entry.Size); } #if !FORCE_ZIP64 if ( entry.CompressedSize >= 0xffffffff ) #endif { 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; }
/// <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="_videoConference.ZipUnzip.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.ConvertToString(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; 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.ConvertToString(buffer, commentLen); } entry.ZipFileIndex = (long)i; entry.Offset = offset; entry.ExternalFileAttributes = (int)externalAttributes; entries_[i] = entry; } }
int FindExistingUpdate(ZipEntry entry) { // TODO: Handling of relative\absolute paths when finding entries? int result = -1; string convertedName = GetTransformedFileName(entry.Name); 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; }
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> /// 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 ) { result = CreateAndInitEncryptionStream(result, entry); } 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; }
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"); } }
internal void SetEntry(ZipEntry entry) { entry_ = entry; entryValid_ = true; bytesTested_ = 0; }
/// <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(string fileName, string entryName, CompressionMethod compressionMethod) { command_ = UpdateCommand.Add; entry_ = new ZipEntry(entryName); #if FORCE_ZIP64 entry_.ForceZip64(); #endif entry_.CompressionMethod = compressionMethod; filename_ = fileName; }
/// <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(string fileName, string entryName) { command_ = UpdateCommand.Add; entry_ = new ZipEntry(entryName); #if FORCE_ZIP64 entry_.ForceZip64(); #endif filename_ = fileName; }
/// <summary> /// Test the 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 test 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 size = ReadLEUint(); long compressedSize = ReadLEUint(); int storedNameLength = ReadLEUshort(); int extraDataLength = ReadLEUshort(); 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 != 20) && (extractVersion != 21) && (extractVersion != 25) && (extractVersion != 27) && (extractVersion != 45) && (extractVersion != 46) && (extractVersion != 50) && (extractVersion != 51) && (extractVersion != 61) && (extractVersion != 62) ) { 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.Reserved) != 0 ) { throw new ZipException("Reserved bit flag cannot bet 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. 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"); } byte[] nameData = new byte[storedNameLength]; StreamUtils.ReadFully(baseStream_, nameData); string localName = ZipConstants.ConvertToString(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"); } byte[] data = new byte[extraDataLength]; StreamUtils.ReadFully(baseStream_, data); ZipExtraData ed = new ZipExtraData(data); // Extra data / zip64 checks if ( ed.Find(1) ) { // Zip64 extra data but extract version 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"); } } } int extraLength = storedNameLength + extraDataLength; return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength; } }
/// <summary> /// Advances to the next entry in the archive /// </summary> /// <returns> /// The next <see cref="ZipEntry">entry</see> in the archive or null if there are no more entries. /// </returns> /// <remarks> /// If the previous entry is still open <see cref="CloseEntry">CloseEntry</see> is called. /// </remarks> /// <exception cref="InvalidOperationException"> /// Input stream is closed /// </exception> /// <exception cref="ZipException"> /// Password is not set, password is invalid, compression method is invalid, /// version required to extract is not supported /// </exception> public ZipEntry GetNextEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry != null) { CloseEntry(); } int header = inputBuffer.ReadLeInt(); if (header == ZipConstants.CentralHeaderSignature || header == ZipConstants.EndOfCentralDirectorySignature || header == ZipConstants.CentralHeaderDigitalSignature || header == ZipConstants.ArchiveExtraDataSignature || header == ZipConstants.Zip64CentralFileHeaderSignature) { // No more individual entries exist Close(); return(null); } // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found // Spanning signature is same as descriptor signature and is untested as yet. if ((header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature)) { header = inputBuffer.ReadLeInt(); } if (header != ZipConstants.LocalHeaderSignature) { throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); } short versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); flags = inputBuffer.ReadLeShort(); method = inputBuffer.ReadLeShort(); uint dostime = (uint)inputBuffer.ReadLeInt(); int crc2 = inputBuffer.ReadLeInt(); csize = inputBuffer.ReadLeInt(); size = inputBuffer.ReadLeInt(); int nameLen = inputBuffer.ReadLeShort(); int extraLen = inputBuffer.ReadLeShort(); bool isCrypted = (flags & 1) == 1; byte[] buffer = new byte[nameLen]; inputBuffer.ReadRawBuffer(buffer); string name = ZipConstants.ConvertToString(buffer); entry = new ZipEntry(name, versionRequiredToExtract); entry.Flags = flags; entry.CompressionMethod = (CompressionMethod)method; if ((flags & 8) == 0) { entry.Crc = crc2 & 0xFFFFFFFFL; entry.Size = size & 0xFFFFFFFFL; entry.CompressedSize = csize & 0xFFFFFFFFL; entry.CryptoCheckValue = (byte)(crc2 >> 24); } else { // This allows for GNU, WinZip and possibly other archives, the PKZIP spec // says these values are zero under these circumstances. if (crc2 != 0) { entry.Crc = crc2 & 0xFFFFFFFFL; } if (size != 0) { entry.Size = size & 0xFFFFFFFFL; } if (csize != 0) { entry.CompressedSize = csize & 0xFFFFFFFFL; } entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); } entry.DosTime = dostime; // If local header requires Zip64 is true then the extended header should contain // both values. // Handle extra data if present. This can set/alter some fields of the entry. if (extraLen > 0) { byte[] extra = new byte[extraLen]; inputBuffer.ReadRawBuffer(extra); entry.ExtraData = extra; } entry.ProcessExtraData(true); if (entry.CompressedSize >= 0) { csize = entry.CompressedSize; } if (entry.Size >= 0) { size = entry.Size; } if (method == (int)CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CryptoHeaderSize != size))) { throw new ZipException("Stored, but compressed != uncompressed"); } // Determine how to handle reading of data if this is attempted. if (entry.IsCompressionMethodSupported()) { internalReader = new ReaderDelegate(InitialRead); } else { internalReader = new ReaderDelegate(ReadingNotSupported); } return(entry); }