| //#define Trace | 
 |  | 
 | // ZipEntry.Write.cs | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Copyright (c) 2009-2011 Dino Chiesa | 
 | // All rights reserved. | 
 | // | 
 | // This code module is part of DotNetZip, a zipfile class library. | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This code is licensed under the Microsoft Public License. | 
 | // See the file License.txt for the license details. | 
 | // More info on: http://dotnetzip.codeplex.com | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Last Saved: <2011-July-30 14:55:47> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This module defines logic for writing (saving) the ZipEntry into a | 
 | // zip file. | 
 | // | 
 | // ------------------------------------------------------------------ | 
 |  | 
 |  | 
 | using OfficeOpenXml.Packaging.Ionic.Zlib; | 
 | using System; | 
 | using System.IO; | 
 | using RE = System.Text.RegularExpressions; | 
 |  | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zip | 
 | { | 
 |     internal partial class ZipEntry | 
 |     { | 
 |         internal void WriteCentralDirectoryEntry(Stream s) | 
 |         { | 
 |             byte[] bytes = new byte[4096]; | 
 |             int i = 0; | 
 |             // signature | 
 |             bytes[i++] = (byte)(ZipConstants.ZipDirEntrySignature & 0x000000FF); | 
 |             bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x0000FF00) >> 8); | 
 |             bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x00FF0000) >> 16); | 
 |             bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0xFF000000) >> 24); | 
 |  | 
 |             // Version Made By | 
 |             // workitem 7071 | 
 |             // We must not overwrite the VersionMadeBy field when writing out a zip | 
 |             // archive.  The VersionMadeBy tells the zip reader the meaning of the | 
 |             // File attributes.  Overwriting the VersionMadeBy will result in | 
 |             // inconsistent metadata.  Consider the scenario where the application | 
 |             // opens and reads a zip file that had been created on Linux. Then the | 
 |             // app adds one file to the Zip archive, and saves it.  The file | 
 |             // attributes for all the entries added on Linux will be significant for | 
 |             // Linux.  Therefore the VersionMadeBy for those entries must not be | 
 |             // changed.  Only the entries that are actually created on Windows NTFS | 
 |             // should get the VersionMadeBy indicating Windows/NTFS. | 
 |             bytes[i++] = (byte)(_VersionMadeBy & 0x00FF); | 
 |             bytes[i++] = (byte)((_VersionMadeBy & 0xFF00) >> 8); | 
 |  | 
 |             // Apparently we want to duplicate the extra field here; we cannot | 
 |             // simply zero it out and assume tools and apps will use the right one. | 
 |  | 
 |             ////Int16 extraFieldLengthSave = (short)(_EntryHeader[28] + _EntryHeader[29] * 256); | 
 |             ////_EntryHeader[28] = 0; | 
 |             ////_EntryHeader[29] = 0; | 
 |  | 
 |             // Version Needed, Bitfield, compression method, lastmod, | 
 |             // crc, compressed and uncompressed sizes, filename length and extra field length. | 
 |             // These are all present in the local file header, but they may be zero values there. | 
 |             // So we cannot just copy them. | 
 |  | 
 |             // workitem 11969: Version Needed To Extract in central directory must be | 
 |             // the same as the local entry or MS .NET System.IO.Zip fails read. | 
 |             Int16 vNeeded = (Int16)(VersionNeeded != 0 ? VersionNeeded : 20); | 
 |             // workitem 12964 | 
 |             if (_OutputUsesZip64==null) | 
 |             { | 
 |                 // a zipentry in a zipoutputstream, with zero bytes written | 
 |                 _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always); | 
 |             } | 
 |  | 
 |             Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : vNeeded); | 
 | #if BZIP | 
 |             if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2) | 
 |                 versionNeededToExtract = 46; | 
 | #endif | 
 |  | 
 |             bytes[i++] = (byte)(versionNeededToExtract & 0x00FF); | 
 |             bytes[i++] = (byte)((versionNeededToExtract & 0xFF00) >> 8); | 
 |  | 
 |             bytes[i++] = (byte)(_BitField & 0x00FF); | 
 |             bytes[i++] = (byte)((_BitField & 0xFF00) >> 8); | 
 |  | 
 |             bytes[i++] = (byte)(_CompressionMethod & 0x00FF); | 
 |             bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); | 
 |  | 
 | #if AESCRYPTO | 
 |             if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |             Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 i -= 2; | 
 |                 bytes[i++] = 0x63; | 
 |                 bytes[i++] = 0; | 
 |             } | 
 | #endif | 
 |  | 
 |             bytes[i++] = (byte)(_TimeBlob & 0x000000FF); | 
 |             bytes[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8); | 
 |             bytes[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16); | 
 |             bytes[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24); | 
 |             bytes[i++] = (byte)(_Crc32 & 0x000000FF); | 
 |             bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); | 
 |             bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); | 
 |             bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); | 
 |  | 
 |             int j = 0; | 
 |             if (_OutputUsesZip64.Value) | 
 |             { | 
 |                 // CompressedSize (Int32) and UncompressedSize - all 0xFF | 
 |                 for (j = 0; j < 8; j++) | 
 |                     bytes[i++] = 0xFF; | 
 |             } | 
 |             else | 
 |             { | 
 |                 bytes[i++] = (byte)(_CompressedSize & 0x000000FF); | 
 |                 bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); | 
 |                 bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); | 
 |                 bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); | 
 |  | 
 |                 bytes[i++] = (byte)(_UncompressedSize & 0x000000FF); | 
 |                 bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); | 
 |                 bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); | 
 |                 bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); | 
 |             } | 
 |  | 
 |             byte[] fileNameBytes = GetEncodedFileNameBytes(); | 
 |             Int16 filenameLength = (Int16)fileNameBytes.Length; | 
 |             bytes[i++] = (byte)(filenameLength & 0x00FF); | 
 |             bytes[i++] = (byte)((filenameLength & 0xFF00) >> 8); | 
 |  | 
 |             // do this again because now we have real data | 
 |             _presumeZip64 = _OutputUsesZip64.Value; | 
 |  | 
 |             // workitem 11131 | 
 |             // | 
 |             // cannot generate the extra field again, here's why: In the case of a | 
 |             // zero-byte entry, which uses encryption, DotNetZip will "remove" the | 
 |             // encryption from the entry.  It does this in PostProcessOutput; it | 
 |             // modifies the entry header, and rewrites it, resetting the Bitfield | 
 |             // (one bit indicates encryption), and potentially resetting the | 
 |             // compression method - for AES the Compression method is 0x63, and it | 
 |             // would get reset to zero (no compression).  It then calls SetLength() | 
 |             // to truncate the stream to remove the encryption header (12 bytes for | 
 |             // AES256).  But, it leaves the previously-generated "Extra Field" | 
 |             // metadata (11 bytes) for AES in the entry header. This extra field | 
 |             // data is now "orphaned" - it refers to AES encryption when in fact no | 
 |             // AES encryption is used. But no problem, the PKWARE spec says that | 
 |             // unrecognized extra fields can just be ignored. ok.  After "removal" | 
 |             // of AES encryption, the length of the Extra Field can remains the | 
 |             // same; it's just that there will be 11 bytes in there that previously | 
 |             // pertained to AES which are now unused. Even the field code is still | 
 |             // there, but it will be unused by readers, as the encryption bit is not | 
 |             // set. | 
 |             // | 
 |             // Re-calculating the Extra field now would produce a block that is 11 | 
 |             // bytes shorter, and that mismatch - between the extra field in the | 
 |             // local header and the extra field in the Central Directory - would | 
 |             // cause problems. (where? why? what problems?)  So we can't do | 
 |             // that. It's all good though, because though the content may have | 
 |             // changed, the length definitely has not. Also, the _EntryHeader | 
 |             // contains the "updated" extra field (after PostProcessOutput) at | 
 |             // offset (30 + filenameLength). | 
 |  | 
 |             _Extra = ConstructExtraField(true); | 
 |  | 
 |             Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length); | 
 |             bytes[i++] = (byte)(extraFieldLength & 0x00FF); | 
 |             bytes[i++] = (byte)((extraFieldLength & 0xFF00) >> 8); | 
 |  | 
 |             // File (entry) Comment Length | 
 |             // the _CommentBytes private field was set during WriteHeader() | 
 |             int commentLength = (_CommentBytes == null) ? 0 : _CommentBytes.Length; | 
 |  | 
 |             // the size of our buffer defines the max length of the comment we can write | 
 |             if (commentLength + i > bytes.Length) commentLength = bytes.Length - i; | 
 |             bytes[i++] = (byte)(commentLength & 0x00FF); | 
 |             bytes[i++] = (byte)((commentLength & 0xFF00) >> 8); | 
 |  | 
 |             // Disk number start | 
 |             bool segmented = (this._container.ZipFile != null) && | 
 |                 (this._container.ZipFile.MaxOutputSegmentSize != 0); | 
 |             if (segmented) // workitem 13915 | 
 |             { | 
 |                 // Emit nonzero disknumber only if saving segmented archive. | 
 |                 bytes[i++] = (byte)(_diskNumber & 0x00FF); | 
 |                 bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8); | 
 |             } | 
 |             else | 
 |             { | 
 |                 // If reading a segmneted archive and saving to a regular archive, | 
 |                 // ZipEntry._diskNumber will be non-zero but it should be saved as | 
 |                 // zero. | 
 |                 bytes[i++] = 0; | 
 |                 bytes[i++] = 0; | 
 |             } | 
 |  | 
 |             // internal file attrs | 
 |             // workitem 7801 | 
 |             bytes[i++] = (byte)((_IsText) ? 1 : 0); // lo bit: filetype hint.  0=bin, 1=txt. | 
 |             bytes[i++] = 0; | 
 |  | 
 |             // external file attrs | 
 |             // workitem 7071 | 
 |             bytes[i++] = (byte)(_ExternalFileAttrs & 0x000000FF); | 
 |             bytes[i++] = (byte)((_ExternalFileAttrs & 0x0000FF00) >> 8); | 
 |             bytes[i++] = (byte)((_ExternalFileAttrs & 0x00FF0000) >> 16); | 
 |             bytes[i++] = (byte)((_ExternalFileAttrs & 0xFF000000) >> 24); | 
 |  | 
 |             // workitem 11131 | 
 |             // relative offset of local header. | 
 |             // | 
 |             // If necessary to go to 64-bit value, then emit 0xFFFFFFFF, | 
 |             // else write out the value. | 
 |             // | 
 |             // Even if zip64 is required for other reasons - number of the entry | 
 |             // > 65534, or uncompressed size of the entry > MAX_INT32, the ROLH | 
 |             // need not be stored in a 64-bit field . | 
 |             if (_RelativeOffsetOfLocalHeader > 0xFFFFFFFFL) // _OutputUsesZip64.Value | 
 |             { | 
 |                 bytes[i++] = 0xFF; | 
 |                 bytes[i++] = 0xFF; | 
 |                 bytes[i++] = 0xFF; | 
 |                 bytes[i++] = 0xFF; | 
 |             } | 
 |             else | 
 |             { | 
 |                 bytes[i++] = (byte)(_RelativeOffsetOfLocalHeader & 0x000000FF); | 
 |                 bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x0000FF00) >> 8); | 
 |                 bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x00FF0000) >> 16); | 
 |                 bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0xFF000000) >> 24); | 
 |             } | 
 |  | 
 |             // actual filename | 
 |             Buffer.BlockCopy(fileNameBytes, 0, bytes, i, filenameLength); | 
 |             i += filenameLength; | 
 |  | 
 |             // "Extra field" | 
 |             if (_Extra != null) | 
 |             { | 
 |                 // workitem 11131 | 
 |                 // | 
 |                 // copy from EntryHeader if available - it may have been updated. | 
 |                 // if not, copy from Extra. This would be unnecessary if I just | 
 |                 // updated the Extra field when updating EntryHeader, in | 
 |                 // PostProcessOutput. | 
 |  | 
 |                 //?? I don't understand why I wouldn't want to just use | 
 |                 // the recalculated Extra field. ?? | 
 |  | 
 |                 // byte[] h = _EntryHeader ?? _Extra; | 
 |                 // int offx = (h == _EntryHeader) ? 30 + filenameLength : 0; | 
 |                 // Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength); | 
 |                 // i += extraFieldLength; | 
 |  | 
 |                 byte[] h = _Extra; | 
 |                 int offx = 0; | 
 |                 Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength); | 
 |                 i += extraFieldLength; | 
 |             } | 
 |  | 
 |             // file (entry) comment | 
 |             if (commentLength != 0) | 
 |             { | 
 |                 // now actually write the comment itself into the byte buffer | 
 |                 Buffer.BlockCopy(_CommentBytes, 0, bytes, i, commentLength); | 
 |                 // for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++) | 
 |                 //     bytes[i + j] = _CommentBytes[j]; | 
 |                 i += commentLength; | 
 |             } | 
 |  | 
 |             s.Write(bytes, 0, i); | 
 |         } | 
 |  | 
 |  | 
 | #if INFOZIP_UTF8 | 
 |         static private bool FileNameIsUtf8(char[] FileNameChars) | 
 |         { | 
 |             bool isUTF8 = false; | 
 |             bool isUnicode = false; | 
 |             for (int j = 0; j < FileNameChars.Length; j++) | 
 |             { | 
 |                 byte[] b = System.BitConverter.GetBytes(FileNameChars[j]); | 
 |                 isUnicode |= (b.Length != 2); | 
 |                 isUnicode |= (b[1] != 0); | 
 |                 isUTF8 |= ((b[0] & 0x80) != 0); | 
 |             } | 
 |  | 
 |             return isUTF8; | 
 |         } | 
 | #endif | 
 |  | 
 |  | 
 |         private byte[] ConstructExtraField(bool forCentralDirectory) | 
 |         { | 
 |             var listOfBlocks = new System.Collections.Generic.List<byte[]>(); | 
 |             byte[] block; | 
 |  | 
 |             // Conditionally emit an extra field with Zip64 information.  If the | 
 |             // Zip64 option is Always, we emit the field, before knowing that it's | 
 |             // necessary.  Later, if it turns out this entry does not need zip64, | 
 |             // we'll set the header ID to rubbish and the data will be ignored. | 
 |             // This results in additional overhead metadata in the zip file, but | 
 |             // it will be small in comparison to the entry data. | 
 |             // | 
 |             // On the other hand if the Zip64 option is AsNecessary and it's NOT | 
 |             // for the central directory, then we do the same thing.  Or, if the | 
 |             // Zip64 option is AsNecessary and it IS for the central directory, | 
 |             // and the entry requires zip64, then emit the header. | 
 |             if (_container.Zip64 == Zip64Option.Always || | 
 |                 (_container.Zip64 == Zip64Option.AsNecessary && | 
 |                  (!forCentralDirectory || _entryRequiresZip64.Value))) | 
 |             { | 
 |                 // add extra field for zip64 here | 
 |                 // workitem 7924 | 
 |                 int sz = 4 + (forCentralDirectory ? 28 : 16); | 
 |                 block = new byte[sz]; | 
 |                 int i = 0; | 
 |  | 
 |                 if (_presumeZip64 || forCentralDirectory) | 
 |                 { | 
 |                     // HeaderId = always use zip64 extensions. | 
 |                     block[i++] = 0x01; | 
 |                     block[i++] = 0x00; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // HeaderId = dummy data now, maybe set to 0x0001 (ZIP64) later. | 
 |                     block[i++] = 0x99; | 
 |                     block[i++] = 0x99; | 
 |                 } | 
 |  | 
 |                 // DataSize | 
 |                 block[i++] = (byte)(sz - 4);  // decimal 28 or 16  (workitem 7924) | 
 |                 block[i++] = 0x00; | 
 |  | 
 |                 // The actual metadata - we may or may not have real values yet... | 
 |  | 
 |                 // uncompressed size | 
 |                 Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, block, i, 8); | 
 |                 i += 8; | 
 |                 // compressed size | 
 |                 Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, block, i, 8); | 
 |                 i += 8; | 
 |  | 
 |                 // workitem 7924 - only include this if the "extra" field is for | 
 |                 // use in the central directory.  It is unnecessary and not useful | 
 |                 // for local header; makes WinZip choke. | 
 |                 if (forCentralDirectory) | 
 |                 { | 
 |                     // relative offset | 
 |                     Array.Copy(BitConverter.GetBytes(_RelativeOffsetOfLocalHeader), 0, block, i, 8); | 
 |                     i += 8; | 
 |  | 
 |                     // starting disk number | 
 |                     Array.Copy(BitConverter.GetBytes(0), 0, block, i, 4); | 
 |                 } | 
 |                 listOfBlocks.Add(block); | 
 |             } | 
 |  | 
 |  | 
 | #if AESCRYPTO | 
 |             if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                 Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 block = new byte[4 + 7]; | 
 |                 int i = 0; | 
 |                 // extra field for WinZip AES | 
 |                 // header id | 
 |                 block[i++] = 0x01; | 
 |                 block[i++] = 0x99; | 
 |  | 
 |                 // data size | 
 |                 block[i++] = 0x07; | 
 |                 block[i++] = 0x00; | 
 |  | 
 |                 // vendor number | 
 |                 block[i++] = 0x01;  // AE-1 - means "Verify CRC" | 
 |                 block[i++] = 0x00; | 
 |  | 
 |                 // vendor id "AE" | 
 |                 block[i++] = 0x41; | 
 |                 block[i++] = 0x45; | 
 |  | 
 |                 // key strength | 
 |                 int keystrength = GetKeyStrengthInBits(Encryption); | 
 |                 if (keystrength == 128) | 
 |                     block[i] = 1; | 
 |                 else if (keystrength == 256) | 
 |                     block[i] = 3; | 
 |                 else | 
 |                     block[i] = 0xFF; | 
 |                 i++; | 
 |  | 
 |                 // actual compression method | 
 |                 block[i++] = (byte)(_CompressionMethod & 0x00FF); | 
 |                 block[i++] = (byte)(_CompressionMethod & 0xFF00); | 
 |  | 
 |                 listOfBlocks.Add(block); | 
 |             } | 
 | #endif | 
 |  | 
 |             if (_ntfsTimesAreSet && _emitNtfsTimes) | 
 |             { | 
 |                 block = new byte[32 + 4]; | 
 |                 // HeaderId   2 bytes    0x000a == NTFS times | 
 |                 // Datasize   2 bytes    32 | 
 |                 // reserved   4 bytes    ?? don't care | 
 |                 // timetag    2 bytes    0x0001 == NTFS time | 
 |                 // size       2 bytes    24 == 8 bytes each for ctime, mtime, atime | 
 |                 // mtime      8 bytes    win32 ticks since win32epoch | 
 |                 // atime      8 bytes    win32 ticks since win32epoch | 
 |                 // ctime      8 bytes    win32 ticks since win32epoch | 
 |                 int i = 0; | 
 |                 // extra field for NTFS times | 
 |                 // header id | 
 |                 block[i++] = 0x0a; | 
 |                 block[i++] = 0x00; | 
 |  | 
 |                 // data size | 
 |                 block[i++] = 32; | 
 |                 block[i++] = 0; | 
 |  | 
 |                 i += 4; // reserved | 
 |  | 
 |                 // time tag | 
 |                 block[i++] = 0x01; | 
 |                 block[i++] = 0x00; | 
 |  | 
 |                 // data size (again) | 
 |                 block[i++] = 24; | 
 |                 block[i++] = 0; | 
 |  | 
 |                 Int64 z = _Mtime.ToFileTime(); | 
 |                 Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); | 
 |                 i += 8; | 
 |                 z = _Atime.ToFileTime(); | 
 |                 Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); | 
 |                 i += 8; | 
 |                 z = _Ctime.ToFileTime(); | 
 |                 Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); | 
 |                 i += 8; | 
 |  | 
 |                 listOfBlocks.Add(block); | 
 |             } | 
 |  | 
 |             if (_ntfsTimesAreSet && _emitUnixTimes) | 
 |             { | 
 |                 int len = 5 + 4; | 
 |                 if (!forCentralDirectory) len += 8; | 
 |  | 
 |                 block = new byte[len]; | 
 |                 // local form: | 
 |                 // -------------- | 
 |                 // HeaderId   2 bytes    0x5455 == unix timestamp | 
 |                 // Datasize   2 bytes    13 | 
 |                 // flags      1 byte     7 (low three bits all set) | 
 |                 // mtime      4 bytes    seconds since unix epoch | 
 |                 // atime      4 bytes    seconds since unix epoch | 
 |                 // ctime      4 bytes    seconds since unix epoch | 
 |                 // | 
 |                 // central directory form: | 
 |                 //--------------------------------- | 
 |                 // HeaderId   2 bytes    0x5455 == unix timestamp | 
 |                 // Datasize   2 bytes    5 | 
 |                 // flags      1 byte     7 (low three bits all set) | 
 |                 // mtime      4 bytes    seconds since unix epoch | 
 |                 // | 
 |                 int i = 0; | 
 |                 // extra field for "unix" times | 
 |                 // header id | 
 |                 block[i++] = 0x55; | 
 |                 block[i++] = 0x54; | 
 |  | 
 |                 // data size | 
 |                 block[i++] = unchecked((byte)(len - 4)); | 
 |                 block[i++] = 0; | 
 |  | 
 |                 // flags | 
 |                 block[i++] = 0x07; | 
 |  | 
 |                 Int32 z = unchecked((int)((_Mtime - _unixEpoch).TotalSeconds)); | 
 |                 Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); | 
 |                 i += 4; | 
 |                 if (!forCentralDirectory) | 
 |                 { | 
 |                     z = unchecked((int)((_Atime - _unixEpoch).TotalSeconds)); | 
 |                     Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); | 
 |                     i += 4; | 
 |                     z = unchecked((int)((_Ctime - _unixEpoch).TotalSeconds)); | 
 |                     Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); | 
 |                     i += 4; | 
 |                 } | 
 |                 listOfBlocks.Add(block); | 
 |             } | 
 |  | 
 |  | 
 |             // inject other blocks here... | 
 |  | 
 |  | 
 |             // concatenate any blocks we've got: | 
 |             byte[] aggregateBlock = null; | 
 |             if (listOfBlocks.Count > 0) | 
 |             { | 
 |                 int totalLength = 0; | 
 |                 int i, current = 0; | 
 |                 for (i = 0; i < listOfBlocks.Count; i++) | 
 |                     totalLength += listOfBlocks[i].Length; | 
 |                 aggregateBlock = new byte[totalLength]; | 
 |                 for (i = 0; i < listOfBlocks.Count; i++) | 
 |                 { | 
 |                     System.Array.Copy(listOfBlocks[i], 0, aggregateBlock, current, listOfBlocks[i].Length); | 
 |                     current += listOfBlocks[i].Length; | 
 |                 } | 
 |             } | 
 |  | 
 |             return aggregateBlock; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         // private System.Text.Encoding GenerateCommentBytes() | 
 |         // { | 
 |         //     var getEncoding = new Func<System.Text.Encoding>({ | 
 |         //     switch (AlternateEncodingUsage) | 
 |         //     { | 
 |         //         case ZipOption.Always: | 
 |         //             return AlternateEncoding; | 
 |         //         case ZipOption.Never: | 
 |         //             return ibm437; | 
 |         //     } | 
 |         //     var cb = ibm437.GetBytes(_Comment); | 
 |         //     // need to use this form of GetString() for .NET CF | 
 |         //     string s1 = ibm437.GetString(cb, 0, cb.Length); | 
 |         //     if (s1 == _Comment) | 
 |         //         return ibm437; | 
 |         //     return AlternateEncoding; | 
 |         //     }); | 
 |         // | 
 |         //     var encoding = getEncoding(); | 
 |         //     _CommentBytes = encoding.GetBytes(_Comment); | 
 |         //     return encoding; | 
 |         // } | 
 |  | 
 |  | 
 |         private string NormalizeFileName() | 
 |         { | 
 |             // here, we need to flip the backslashes to forward-slashes, | 
 |             // also, we need to trim the \\server\share syntax from any UNC path. | 
 |             // and finally, we need to remove any leading .\ | 
 |  | 
 |             string SlashFixed = FileName.Replace("\\", "/"); | 
 |             string s1 = null; | 
 |             if ((_TrimVolumeFromFullyQualifiedPaths) && (FileName.Length >= 3) | 
 |                 && (FileName[1] == ':') && (SlashFixed[2] == '/')) | 
 |             { | 
 |                 // trim off volume letter, colon, and slash | 
 |                 s1 = SlashFixed.Substring(3); | 
 |             } | 
 |             else if ((FileName.Length >= 4) | 
 |                      && ((SlashFixed[0] == '/') && (SlashFixed[1] == '/'))) | 
 |             { | 
 |                 int n = SlashFixed.IndexOf('/', 2); | 
 |                 if (n == -1) | 
 |                     throw new ArgumentException("The path for that entry appears to be badly formatted"); | 
 |                 s1 = SlashFixed.Substring(n + 1); | 
 |             } | 
 |             else if ((FileName.Length >= 3) | 
 |                      && ((SlashFixed[0] == '.') && (SlashFixed[1] == '/'))) | 
 |             { | 
 |                 // trim off dot and slash | 
 |                 s1 = SlashFixed.Substring(2); | 
 |             } | 
 |             else | 
 |             { | 
 |                 s1 = SlashFixed; | 
 |             } | 
 |             return s1; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   generate and return a byte array that encodes the filename | 
 |         ///   for the entry. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     side effects: generate and store into _CommentBytes the | 
 |         ///     byte array for any comment attached to the entry. Also | 
 |         ///     sets _actualEncoding to indicate the actual encoding | 
 |         ///     used. The same encoding is used for both filename and | 
 |         ///     comment. | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         private byte[] GetEncodedFileNameBytes() | 
 |         { | 
 |             // workitem 6513 | 
 |             var s1 = NormalizeFileName(); | 
 |  | 
 |             switch(AlternateEncodingUsage) | 
 |             { | 
 |                 case ZipOption.Always: | 
 |                     if (!(_Comment == null || _Comment.Length == 0)) | 
 |                         _CommentBytes = AlternateEncoding.GetBytes(_Comment); | 
 |                     _actualEncoding = AlternateEncoding; | 
 |                     return AlternateEncoding.GetBytes(s1); | 
 |  | 
 |                 case ZipOption.Never: | 
 |                     if (!(_Comment == null || _Comment.Length == 0)) | 
 |                         _CommentBytes = ibm437.GetBytes(_Comment); | 
 |                     _actualEncoding = ibm437; | 
 |                     return ibm437.GetBytes(s1); | 
 |             } | 
 |  | 
 |             // arriving here means AlternateEncodingUsage is "AsNecessary" | 
 |  | 
 |             // case ZipOption.AsNecessary: | 
 |             // workitem 6513: when writing, use the alternative encoding | 
 |             // only when _actualEncoding is not yet set (it can be set | 
 |             // during Read), and when ibm437 will not do. | 
 |  | 
 |             byte[] result = ibm437.GetBytes(s1); | 
 |             // need to use this form of GetString() for .NET CF | 
 |             string s2 = ibm437.GetString(result, 0, result.Length); | 
 |             _CommentBytes = null; | 
 |             if (s2 != s1) | 
 |             { | 
 |                 // Encoding the filename with ibm437 does not allow round-trips. | 
 |                 // Therefore, use the alternate encoding.  Assume it will work, | 
 |                 // no checking of round trips here. | 
 |                 result = AlternateEncoding.GetBytes(s1); | 
 |                 if (_Comment != null && _Comment.Length != 0) | 
 |                     _CommentBytes = AlternateEncoding.GetBytes(_Comment); | 
 |                 _actualEncoding = AlternateEncoding; | 
 |                 return result; | 
 |             } | 
 |  | 
 |             _actualEncoding = ibm437; | 
 |  | 
 |             // Using ibm437, FileName can be encoded without information | 
 |             // loss; now try the Comment. | 
 |  | 
 |             // if there is no comment, use ibm437. | 
 |             if (_Comment == null || _Comment.Length == 0) | 
 |                 return result; | 
 |  | 
 |             // there is a comment. Get the encoded form. | 
 |             byte[] cbytes = ibm437.GetBytes(_Comment); | 
 |             string c2 = ibm437.GetString(cbytes,0,cbytes.Length); | 
 |  | 
 |             // Check for round-trip. | 
 |             if (c2 != Comment) | 
 |             { | 
 |                 // Comment cannot correctly be encoded with ibm437.  Use | 
 |                 // the alternate encoding. | 
 |  | 
 |                 result = AlternateEncoding.GetBytes(s1); | 
 |                 _CommentBytes = AlternateEncoding.GetBytes(_Comment); | 
 |                 _actualEncoding = AlternateEncoding; | 
 |                 return result; | 
 |             } | 
 |  | 
 |             // use IBM437 | 
 |             _CommentBytes = cbytes; | 
 |             return result; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private bool WantReadAgain() | 
 |         { | 
 |             if (_UncompressedSize < 0x10) return false; | 
 |             if (_CompressionMethod == 0x00) return false; | 
 |             if (CompressionLevel == OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel.None) return false; | 
 |             if (_CompressedSize < _UncompressedSize) return false; | 
 |  | 
 |             if (this._Source == ZipEntrySource.Stream && !this._sourceStream.CanSeek) return false; | 
 |  | 
 | #if AESCRYPTO | 
 |             if (_aesCrypto_forWrite != null && (CompressedSize - _aesCrypto_forWrite.SizeOfEncryptionMetadata) <= UncompressedSize + 0x10) return false; | 
 | #endif | 
 |  | 
 |             if (_zipCrypto_forWrite != null && (CompressedSize - 12) <= UncompressedSize) return false; | 
 |  | 
 |             return true; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void MaybeUnsetCompressionMethodForWriting(int cycle) | 
 |         { | 
 |             // if we've already tried with compression... turn it off this time | 
 |             if (cycle > 1) | 
 |             { | 
 |                 _CompressionMethod = 0x0; | 
 |                 return; | 
 |             } | 
 |             // compression for directories = 0x00 (No Compression) | 
 |             if (IsDirectory) | 
 |             { | 
 |                 _CompressionMethod = 0x0; | 
 |                 return; | 
 |             } | 
 |  | 
 |             if (this._Source == ZipEntrySource.ZipFile) | 
 |             { | 
 |                 return; // do nothing | 
 |             } | 
 |  | 
 |             // If __FileDataPosition is zero, then that means we will get the data | 
 |             // from a file or stream. | 
 |  | 
 |             // It is never possible to compress a zero-length file, so we check for | 
 |             // this condition. | 
 |  | 
 |             if (this._Source == ZipEntrySource.Stream) | 
 |             { | 
 |                 // workitem 7742 | 
 |                 if (_sourceStream != null && _sourceStream.CanSeek) | 
 |                 { | 
 |                     // Length prop will throw if CanSeek is false | 
 |                     long fileLength = _sourceStream.Length; | 
 |                     if (fileLength == 0) | 
 |                     { | 
 |                         _CompressionMethod = 0x00; | 
 |                         return; | 
 |                     } | 
 |                 } | 
 |             } | 
 |             else if ((this._Source == ZipEntrySource.FileSystem) && (SharedUtilities.GetFileLength(LocalFileName) == 0L)) | 
 |             { | 
 |                 _CompressionMethod = 0x00; | 
 |                 return; | 
 |             } | 
 |  | 
 |             // Ok, we're getting the data to be compressed from a | 
 |             // non-zero-length file or stream, or a file or stream of | 
 |             // unknown length, and we presume that it is non-zero.  In | 
 |             // that case we check the callback to see if the app wants | 
 |             // to tell us whether to compress or not. | 
 |             if (SetCompression != null) | 
 |                 CompressionLevel = SetCompression(LocalFileName, _FileNameInArchive); | 
 |  | 
 |             // finally, set CompressionMethod to None if CompressionLevel is None | 
 |             if (CompressionLevel == (short)Ionic.Zlib.CompressionLevel.None && | 
 |                 CompressionMethod == Ionic.Zip.CompressionMethod.Deflate) | 
 |                 _CompressionMethod = 0x00; | 
 |  | 
 |             return; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         // write the header info for an entry | 
 |         internal void WriteHeader(Stream s, int cycle) | 
 |         { | 
 |             // Must remember the offset, within the output stream, of this particular | 
 |             // entry header. | 
 |             // | 
 |             // This is for 2 reasons: | 
 |             // | 
 |             //  1. so we can determine the RelativeOffsetOfLocalHeader (ROLH) for | 
 |             //     use in the central directory. | 
 |             //  2. so we can seek backward in case there is an error opening or reading | 
 |             //     the file, and the application decides to skip the file. In this case, | 
 |             //     we need to seek backward in the output stream to allow the next entry | 
 |             //     to be added to the zipfile output stream. | 
 |             // | 
 |             // Normally you would just store the offset before writing to the output | 
 |             // stream and be done with it.  But the possibility to use split archives | 
 |             // makes this approach ineffective.  In split archives, each file or segment | 
 |             // is bound to a max size limit, and each local file header must not span a | 
 |             // segment boundary; it must be written contiguously.  If it will fit in the | 
 |             // current segment, then the ROLH is just the current Position in the output | 
 |             // stream.  If it won't fit, then we need a new file (segment) and the ROLH | 
 |             // is zero. | 
 |             // | 
 |             // But we only can know if it is possible to write a header contiguously | 
 |             // after we know the size of the local header, a size that varies with | 
 |             // things like filename length, comments, and extra fields.  We have to | 
 |             // compute the header fully before knowing whether it will fit. | 
 |             // | 
 |             // That takes care of item #1 above.  Now, regarding #2.  If an error occurs | 
 |             // while computing the local header, we want to just seek backward. The | 
 |             // exception handling logic (in the caller of WriteHeader) uses ROLH to | 
 |             // scroll back. | 
 |             // | 
 |             // All this means we have to preserve the starting offset before computing | 
 |             // the header, and also we have to compute the offset later, to handle the | 
 |             // case of split archives. | 
 |  | 
 |             var counter = s as CountingStream; | 
 |  | 
 |             // workitem 8098: ok (output) | 
 |             // This may change later, for split archives | 
 |  | 
 |             // Don't set _RelativeOffsetOfLocalHeader. Instead, set a temp variable. | 
 |             // This allows for re-streaming, where a zip entry might be read from a | 
 |             // zip archive (and maybe decrypted, and maybe decompressed) and then | 
 |             // written to another zip archive, with different settings for | 
 |             // compression method, compression level, or encryption algorithm. | 
 |             _future_ROLH = (counter != null) | 
 |                 ? counter.ComputedPosition | 
 |                 : s.Position; | 
 |  | 
 |             int j = 0, i = 0; | 
 |  | 
 |             byte[] block = new byte[30]; | 
 |  | 
 |             // signature | 
 |             block[i++] = (byte)(ZipConstants.ZipEntrySignature & 0x000000FF); | 
 |             block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x0000FF00) >> 8); | 
 |             block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x00FF0000) >> 16); | 
 |             block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0xFF000000) >> 24); | 
 |  | 
 |             // Design notes for ZIP64: | 
 |             // | 
 |             // The specification says that the header must include the Compressed | 
 |             // and Uncompressed sizes, as well as the CRC32 value.  When creating | 
 |             // a zip via streamed processing, these quantities are not known until | 
 |             // after the compression is done.  Thus, a typical way to do it is to | 
 |             // insert zeroes for these quantities, then do the compression, then | 
 |             // seek back to insert the appropriate values, then seek forward to | 
 |             // the end of the file data. | 
 |             // | 
 |             // There is also the option of using bit 3 in the GP bitfield - to | 
 |             // specify that there is a data descriptor after the file data | 
 |             // containing these three quantities. | 
 |             // | 
 |             // This works when the size of the quantities is known, either 32-bits | 
 |             // or 64 bits as with the ZIP64 extensions. | 
 |             // | 
 |             // With Zip64, the 4-byte fields are set to 0xffffffff, and there is a | 
 |             // corresponding data block in the "extra field" that contains the | 
 |             // actual Compressed, uncompressed sizes.  (As well as an additional | 
 |             // field, the "Relative Offset of Local Header") | 
 |             // | 
 |             // The problem is when the app desires to use ZIP64 extensions | 
 |             // optionally, only when necessary.  Suppose the library assumes no | 
 |             // zip64 extensions when writing the header, then after compression | 
 |             // finds that the size of the data requires zip64.  At this point, the | 
 |             // header, already written to the file, won't have the necessary data | 
 |             // block in the "extra field".  The size of the entry header is fixed, | 
 |             // so it is not possible to just "add on" the zip64 data block after | 
 |             // compressing the file.  On the other hand, always using zip64 will | 
 |             // break interoperability with many other systems and apps. | 
 |             // | 
 |             // The approach we take is to insert a 32-byte dummy data block in the | 
 |             // extra field, whenever zip64 is to be used "as necessary". This data | 
 |             // block will get the actual zip64 HeaderId and zip64 metadata if | 
 |             // necessary.  If not necessary, the data block will get a meaningless | 
 |             // HeaderId (0x1111), and will be filled with zeroes. | 
 |             // | 
 |             // When zip64 is actually in use, we also need to set the | 
 |             // VersionNeededToExtract field to 45. | 
 |             // | 
 |             // There is one additional wrinkle: using zip64 as necessary conflicts | 
 |             // with output to non-seekable devices.  The header is emitted and | 
 |             // must indicate whether zip64 is in use, before we know if zip64 is | 
 |             // necessary.  Because there is no seeking, the header can never be | 
 |             // changed.  Therefore, on non-seekable devices, | 
 |             // Zip64Option.AsNecessary is the same as Zip64Option.Always. | 
 |             // | 
 |  | 
 |  | 
 |             // version needed- see AppNote.txt. | 
 |             // | 
 |             // need v5.1 for PKZIP strong encryption, or v2.0 for no encryption or | 
 |             // for PK encryption, 4.5 for zip64.  We may reset this later, as | 
 |             // necessary or zip64. | 
 |  | 
 |             _presumeZip64 = (_container.Zip64 == Zip64Option.Always || | 
 |                              (_container.Zip64 == Zip64Option.AsNecessary && !s.CanSeek)); | 
 |             Int16 VersionNeededToExtract = (Int16)(_presumeZip64 ? 45 : 20); | 
 | #if BZIP | 
 |             if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2) | 
 |                 VersionNeededToExtract = 46; | 
 | #endif | 
 |  | 
 |             // (i==4) | 
 |             block[i++] = (byte)(VersionNeededToExtract & 0x00FF); | 
 |             block[i++] = (byte)((VersionNeededToExtract & 0xFF00) >> 8); | 
 |  | 
 |             // Get byte array. Side effect: sets ActualEncoding. | 
 |             // Must determine encoding before setting the bitfield. | 
 |             // workitem 6513 | 
 |             byte[] fileNameBytes = GetEncodedFileNameBytes(); | 
 |             Int16 filenameLength = (Int16)fileNameBytes.Length; | 
 |  | 
 |             // general purpose bitfield | 
 |             // In the current implementation, this library uses only these bits | 
 |             // in the GP bitfield: | 
 |             //  bit 0 = if set, indicates the entry is encrypted | 
 |             //  bit 3 = if set, indicates the CRC, C and UC sizes follow the file data. | 
 |             //  bit 6 = strong encryption - for pkware's meaning of strong encryption | 
 |             //  bit 11 = UTF-8 encoding is used in the comment and filename | 
 |  | 
 |  | 
 |             // Here we set or unset the encryption bit. | 
 |             // _BitField may already be set, as with a ZipEntry added into ZipOutputStream, which | 
 |             // has bit 3 always set. We only want to set one bit | 
 |             if (_Encryption == EncryptionAlgorithm.None) | 
 |                 _BitField &= ~1;  // encryption bit OFF | 
 |             else | 
 |                 _BitField |= 1;   // encryption bit ON | 
 |  | 
 |  | 
 |             // workitem 7941: WinZip does not the "strong encryption" bit  when using AES. | 
 |             // This "Strong Encryption" is a PKWare Strong encryption thing. | 
 |             //                 _BitField |= 0x0020; | 
 |  | 
 |             // set the UTF8 bit if necessary | 
 | #if SILVERLIGHT | 
 |             if (_actualEncoding.WebName == "utf-8") | 
 | #else | 
 |             if (_actualEncoding.CodePage == System.Text.Encoding.UTF8.CodePage) | 
 | #endif | 
 |                 _BitField |= 0x0800; | 
 |  | 
 |             // The PKZIP spec says that if bit 3 is set (0x0008) in the General | 
 |             // Purpose BitField, then the CRC, Compressed size, and uncompressed | 
 |             // size are written directly after the file data. | 
 |             // | 
 |             // These 3 quantities are normally present in the regular zip entry | 
 |             // header. But, they are not knowable until after the compression is | 
 |             // done. So, in the normal case, we | 
 |             // | 
 |             //  - write the header, using zeros for these quantities | 
 |             //  - compress the data, and incidentally compute these quantities. | 
 |             //  - seek back and write the correct values them into the header. | 
 |             // | 
 |             // This is nice because, while it is more complicated to write the zip | 
 |             // file, it is simpler and less error prone to read the zip file, and | 
 |             // as a result more applications can read zip files produced this way, | 
 |             // with those 3 quantities in the header. | 
 |             // | 
 |             // But if seeking in the output stream is not possible, then we need | 
 |             // to set the appropriate bitfield and emit these quantities after the | 
 |             // compressed file data in the output. | 
 |             // | 
 |             // workitem 7216 - having trouble formatting a zip64 file that is | 
 |             // readable by WinZip.  not sure why!  What I found is that setting | 
 |             // bit 3 and following all the implications, the zip64 file is | 
 |             // readable by WinZip 12. and Perl's IO::Compress::Zip .  Perl takes | 
 |             // an interesting approach - it always sets bit 3 if ZIP64 in use. | 
 |             // DotNetZip now does the same; this gives better compatibility with | 
 |             // WinZip 12. | 
 |  | 
 |             if (IsDirectory || cycle == 99) | 
 |             { | 
 |                 // (cycle == 99) indicates a zero-length entry written by ZipOutputStream | 
 |  | 
 |                 _BitField &= ~0x0008;  // unset bit 3 - no "data descriptor" - ever | 
 |                 _BitField &= ~0x0001;  // unset bit 1 - no encryption - ever | 
 |                 Encryption = EncryptionAlgorithm.None; | 
 |                 Password = null; | 
 |             } | 
 |             else if (!s.CanSeek) | 
 |                 _BitField |= 0x0008; | 
 |  | 
 | #if DONT_GO_THERE | 
 |             else if (this.Encryption == EncryptionAlgorithm.PkzipWeak  && | 
 |                      this._Source != ZipEntrySource.ZipFile) | 
 |             { | 
 |                 // Set bit 3 to avoid the double-read perf issue. | 
 |                 // | 
 |                 // When PKZIP encryption is used, byte 11 of the encryption header is | 
 |                 // used as a consistency check. It is normally set to the MSByte of the | 
 |                 // CRC.  But this means the cRC must be known ebfore compression and | 
 |                 // encryption, which means the entire stream has to be read twice.  To | 
 |                 // avoid that, the high-byte of the time blob (when in DOS format) can | 
 |                 // be used for the consistency check (byte 11 in the encryption header). | 
 |                 // But this means the entry must have bit 3 set. | 
 |                 // | 
 |                 // Previously I used a more complex arrangement - using the methods like | 
 |                 // FigureCrc32(), PrepOutputStream() and others, in order to manage the | 
 |                 // seek-back in the source stream.  Why?  Because bit 3 is not always | 
 |                 // friendly with third-party zip tools, like those on the Mac. | 
 |                 // | 
 |                 // This is why this code is still ifdef'd  out. | 
 |                 // | 
 |                 // Might consider making this yet another programmable option - | 
 |                 // AlwaysUseBit3ForPkzip.  But that's for another day. | 
 |                 // | 
 |                 _BitField |= 0x0008; | 
 |             } | 
 | #endif | 
 |  | 
 |             // (i==6) | 
 |             block[i++] = (byte)(_BitField & 0x00FF); | 
 |             block[i++] = (byte)((_BitField & 0xFF00) >> 8); | 
 |  | 
 |             // Here, we want to set values for Compressed Size, Uncompressed Size, | 
 |             // and CRC.  If we have __FileDataPosition as not -1 (zero is a valid | 
 |             // FDP), then that means we are reading this zip entry from a zip | 
 |             // file, and we have good values for those quantities. | 
 |             // | 
 |             // If _FileDataPosition is -1, then we are constructing this Entry | 
 |             // from nothing.  We zero those quantities now, and we will compute | 
 |             // actual values for the three quantities later, when we do the | 
 |             // compression, and then seek back to write them into the appropriate | 
 |             // place in the header. | 
 |             if (this.__FileDataPosition == -1) | 
 |             { | 
 |                 //_UncompressedSize = 0; // do not unset - may need this value for restream | 
 |                 // _Crc32 = 0;           // ditto | 
 |                 _CompressedSize = 0; | 
 |                 _crcCalculated = false; | 
 |             } | 
 |  | 
 |             // set compression method here | 
 |             MaybeUnsetCompressionMethodForWriting(cycle); | 
 |  | 
 |             // (i==8) compression method | 
 |             block[i++] = (byte)(_CompressionMethod & 0x00FF); | 
 |             block[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); | 
 |  | 
 |             if (cycle == 99) | 
 |             { | 
 |                 // (cycle == 99) indicates a zero-length entry written by ZipOutputStream | 
 |                 SetZip64Flags(); | 
 |             } | 
 |  | 
 | #if AESCRYPTO | 
 |             else if (Encryption == EncryptionAlgorithm.WinZipAes128 || Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 i -= 2; | 
 |                 block[i++] = 0x63; | 
 |                 block[i++] = 0; | 
 |             } | 
 | #endif | 
 |  | 
 |             // LastMod | 
 |             _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified); | 
 |  | 
 |             // (i==10) time blob | 
 |             block[i++] = (byte)(_TimeBlob & 0x000000FF); | 
 |             block[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8); | 
 |             block[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16); | 
 |             block[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24); | 
 |  | 
 |             // (i==14) CRC - if source==filesystem, this is zero now, actual value | 
 |             // will be calculated later.  if source==archive, this is a bonafide | 
 |             // value. | 
 |             block[i++] = (byte)(_Crc32 & 0x000000FF); | 
 |             block[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); | 
 |             block[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); | 
 |             block[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); | 
 |  | 
 |             if (_presumeZip64) | 
 |             { | 
 |                 // (i==18) CompressedSize (Int32) and UncompressedSize - all 0xFF for now | 
 |                 for (j = 0; j < 8; j++) | 
 |                     block[i++] = 0xFF; | 
 |             } | 
 |             else | 
 |             { | 
 |                 // (i==18) CompressedSize (Int32) - this value may or may not be | 
 |                 // bonafide.  if source == filesystem, then it is zero, and we'll | 
 |                 // learn it after we compress.  if source == archive, then it is | 
 |                 // bonafide data. | 
 |                 block[i++] = (byte)(_CompressedSize & 0x000000FF); | 
 |                 block[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); | 
 |                 block[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); | 
 |                 block[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); | 
 |  | 
 |                 // (i==22) UncompressedSize (Int32) - this value may or may not be | 
 |                 // bonafide. | 
 |                 block[i++] = (byte)(_UncompressedSize & 0x000000FF); | 
 |                 block[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); | 
 |                 block[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); | 
 |                 block[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); | 
 |             } | 
 |  | 
 |             // (i==26) filename length (Int16) | 
 |             block[i++] = (byte)(filenameLength & 0x00FF); | 
 |             block[i++] = (byte)((filenameLength & 0xFF00) >> 8); | 
 |  | 
 |             _Extra = ConstructExtraField(false); | 
 |  | 
 |             // (i==28) extra field length (short) | 
 |             Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length); | 
 |             block[i++] = (byte)(extraFieldLength & 0x00FF); | 
 |             block[i++] = (byte)((extraFieldLength & 0xFF00) >> 8); | 
 |  | 
 |             // workitem 13542 | 
 |             byte[] bytes = new byte[i + filenameLength + extraFieldLength]; | 
 |  | 
 |             // get the fixed portion | 
 |             Buffer.BlockCopy(block, 0, bytes, 0, i); | 
 |             //for (j = 0; j < i; j++) bytes[j] = block[j]; | 
 |  | 
 |             // The filename written to the archive. | 
 |             Buffer.BlockCopy(fileNameBytes, 0, bytes, i, fileNameBytes.Length); | 
 |             // for (j = 0; j < fileNameBytes.Length; j++) | 
 |             //     bytes[i + j] = fileNameBytes[j]; | 
 |  | 
 |             i += fileNameBytes.Length; | 
 |  | 
 |             // "Extra field" | 
 |             if (_Extra != null) | 
 |             { | 
 |                 Buffer.BlockCopy(_Extra, 0, bytes, i, _Extra.Length); | 
 |                 // for (j = 0; j < _Extra.Length; j++) | 
 |                 //     bytes[i + j] = _Extra[j]; | 
 |                 i += _Extra.Length; | 
 |             } | 
 |  | 
 |             _LengthOfHeader = i; | 
 |  | 
 |             // handle split archives | 
 |             var zss = s as ZipSegmentedStream; | 
 |             if (zss != null) | 
 |             { | 
 |                 zss.ContiguousWrite = true; | 
 |                 UInt32 requiredSegment = zss.ComputeSegment(i); | 
 |                 if (requiredSegment != zss.CurrentSegment) | 
 |                     _future_ROLH = 0; // rollover! | 
 |                 else | 
 |                     _future_ROLH = zss.Position; | 
 |  | 
 |                 _diskNumber = requiredSegment; | 
 |             } | 
 |  | 
 |             // validate the ZIP64 usage | 
 |             if (_container.Zip64 == Zip64Option.Never && (uint)_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF) | 
 |                 throw new ZipException("Offset within the zip archive exceeds 0xFFFFFFFF. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); | 
 |  | 
 |  | 
 |             // finally, write the header to the stream | 
 |             s.Write(bytes, 0, i); | 
 |  | 
 |             // now that the header is written, we can turn off the contiguous write restriction. | 
 |             if (zss != null) | 
 |                 zss.ContiguousWrite = false; | 
 |  | 
 |             // Preserve this header data, we'll use it again later. | 
 |             // ..when seeking backward, to write again, after we have the Crc, compressed | 
 |             //   and uncompressed sizes. | 
 |             // ..and when writing the central directory structure. | 
 |             _EntryHeader = bytes; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         private Int32 FigureCrc32() | 
 |         { | 
 |             if (_crcCalculated == false) | 
 |             { | 
 |                 Stream input = null; | 
 |                 // get the original stream: | 
 |                 if (this._Source == ZipEntrySource.WriteDelegate) | 
 |                 { | 
 |                     var output = new Ionic.Crc.CrcCalculatorStream(Stream.Null); | 
 |                     // allow the application to write the data | 
 |                     this._WriteDelegate(this.FileName, output); | 
 |                     _Crc32 = output.Crc; | 
 |                 } | 
 |                 else if (this._Source == ZipEntrySource.ZipFile) | 
 |                 { | 
 |                     // nothing to do - the CRC is already set | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     if (this._Source == ZipEntrySource.Stream) | 
 |                     { | 
 |                         PrepSourceStream(); | 
 |                         input = this._sourceStream; | 
 |                     } | 
 |                     else if (this._Source == ZipEntrySource.JitStream) | 
 |                     { | 
 |                         // allow the application to open the stream | 
 |                         if (this._sourceStream == null) | 
 |                             _sourceStream = this._OpenDelegate(this.FileName); | 
 |                         PrepSourceStream(); | 
 |                         input = this._sourceStream; | 
 |                     } | 
 |                     else if (this._Source == ZipEntrySource.ZipOutputStream) | 
 |                     { | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         //input = File.OpenRead(LocalFileName); | 
 |                         input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); | 
 |                     } | 
 |  | 
 |                     var crc32 = new Ionic.Crc.CRC32(); | 
 |                     _Crc32 = crc32.GetCrc32(input); | 
 |  | 
 |                     if (_sourceStream == null) | 
 |                     { | 
 | #if NETCF | 
 |                         input.Close(); | 
 | #else | 
 |                         input.Dispose(); | 
 | #endif | 
 |                     } | 
 |                 } | 
 |                 _crcCalculated = true; | 
 |             } | 
 |             return _Crc32; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Stores the position of the entry source stream, or, if the position is | 
 |         ///   already stored, seeks to that position. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <remarks> | 
 |         /// <para> | 
 |         ///   This method is called in prep for reading the source stream.  If PKZIP | 
 |         ///   encryption is used, then we need to calc the CRC32 before doing the | 
 |         ///   encryption, because the CRC is used in the 12th byte of the PKZIP | 
 |         ///   encryption header.  So, we need to be able to seek backward in the source | 
 |         ///   when saving the ZipEntry. This method is called from the place that | 
 |         ///   calculates the CRC, and also from the method that does the encryption of | 
 |         ///   the file data. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The first time through, this method sets the _sourceStreamOriginalPosition | 
 |         ///   field. Subsequent calls to this method seek to that position. | 
 |         /// </para> | 
 |         /// </remarks> | 
 |         private void PrepSourceStream() | 
 |         { | 
 |             if (_sourceStream == null) | 
 |                 throw new ZipException(String.Format("The input stream is null for entry '{0}'.", FileName)); | 
 |  | 
 |             if (this._sourceStreamOriginalPosition != null) | 
 |             { | 
 |                 // this will happen the 2nd cycle through, if the stream is seekable | 
 |                 this._sourceStream.Position = this._sourceStreamOriginalPosition.Value; | 
 |             } | 
 |             else if (this._sourceStream.CanSeek) | 
 |             { | 
 |                 // this will happen the first cycle through, if seekable | 
 |                 this._sourceStreamOriginalPosition = new Nullable<Int64>(this._sourceStream.Position); | 
 |             } | 
 |             else if (this.Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |             { | 
 |                 // In general, using PKZIP encryption on a a zip entry whose input | 
 |                 // comes from a non-seekable stream, is tricky.  Here's why: | 
 |                 // | 
 |                 // Byte 11 of the PKZIP encryption header is used for password | 
 |                 // validation and consistency checknig. | 
 |                 // | 
 |                 // Normally, the highest byte of the CRC is used as the 11th (last) byte | 
 |                 // in the PKZIP encryption header. This means the CRC must be known | 
 |                 // before encryption is performed. Normally that means we read the full | 
 |                 // data stream, compute the CRC, then seek back and read it again for | 
 |                 // the compression+encryption phase. Obviously this is bad for | 
 |                 // performance with a large input file. | 
 |                 // | 
 |                 // There's a twist in the ZIP spec (actually documented only in infozip | 
 |                 // code, not in the spec itself) that allows the high-order byte of the | 
 |                 // last modified time for the entry, when the lastmod time is in packed | 
 |                 // (DOS) format, to be used for Byte 11 in the encryption header. In | 
 |                 // this case, the bit 3 "data descriptor" must be used. | 
 |                 // | 
 |                 // An intelligent implementation would therefore force the use of the | 
 |                 // bit 3 data descriptor when PKZIP encryption is in use, regardless. | 
 |                 // This avoids the double-read of the stream to be encrypted.  So far, | 
 |                 // DotNetZip doesn't do that; it just punts when the input stream is | 
 |                 // non-seekable, and the output does not use Bit 3. | 
 |                 // | 
 |                 // The other option is to use the CRC when it is already available, eg, | 
 |                 // when the source for the data is a ZipEntry (when the zip file is | 
 |                 // being updated). In this case we already know the CRC and can just use | 
 |                 // what we know. | 
 |  | 
 |                 if (this._Source != ZipEntrySource.ZipFile && ((this._BitField & 0x0008) != 0x0008)) | 
 |                     throw new ZipException("It is not possible to use PKZIP encryption on a non-seekable input stream"); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Copy metadata that may have been changed by the app.  We do this when | 
 |         /// resetting the zipFile instance.  If the app calls Save() on a ZipFile, then | 
 |         /// tries to party on that file some more, we may need to Reset() it , which | 
 |         /// means re-reading the entries and then copying the metadata.  I think. | 
 |         /// </summary> | 
 |         internal void CopyMetaData(ZipEntry source) | 
 |         { | 
 |             this.__FileDataPosition = source.__FileDataPosition; | 
 |             this.CompressionMethod = source.CompressionMethod; | 
 |             this._CompressionMethod_FromZipFile = source._CompressionMethod_FromZipFile; | 
 |             this._CompressedFileDataSize = source._CompressedFileDataSize; | 
 |             this._UncompressedSize = source._UncompressedSize; | 
 |             this._BitField = source._BitField; | 
 |             this._Source = source._Source; | 
 |             this._LastModified = source._LastModified; | 
 |             this._Mtime = source._Mtime; | 
 |             this._Atime = source._Atime; | 
 |             this._Ctime = source._Ctime; | 
 |             this._ntfsTimesAreSet = source._ntfsTimesAreSet; | 
 |             this._emitUnixTimes = source._emitUnixTimes; | 
 |             this._emitNtfsTimes = source._emitNtfsTimes; | 
 |         } | 
 |  | 
 |  | 
 |         private void OnWriteBlock(Int64 bytesXferred, Int64 totalBytesToXfer) | 
 |         { | 
 |             if (_container.ZipFile != null) | 
 |                 _ioOperationCanceled = _container.ZipFile.OnSaveBlock(this, bytesXferred, totalBytesToXfer); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void _WriteEntryData(Stream s) | 
 |         { | 
 |             // Read in the data from the input stream (often a file in the filesystem), | 
 |             // and write it to the output stream, calculating a CRC on it as we go. | 
 |             // We will also compress and encrypt as necessary. | 
 |  | 
 |             Stream input = null; | 
 |             long fdp = -1L; | 
 |             try | 
 |             { | 
 |                 // Want to record the position in the zip file of the zip entry | 
 |                 // data (as opposed to the metadata).  s.Position may fail on some | 
 |                 // write-only streams, eg stdout or System.Web.HttpResponseStream. | 
 |                 // We swallow that exception, because we don't care, in that case. | 
 |                 // But, don't set __FileDataPosition directly.  It may be needed | 
 |                 // to READ the zip entry from the zip file, if this is a | 
 |                 // "re-stream" situation. In other words if the zip entry has | 
 |                 // changed compression level, or compression method, or (maybe?) | 
 |                 // encryption algorithm.  In that case if the original entry is | 
 |                 // encrypted, we need __FileDataPosition to be the value for the | 
 |                 // input zip file.  This s.Position is for the output zipfile.  So | 
 |                 // we copy fdp to __FileDataPosition after this entry has been | 
 |                 // (maybe) restreamed. | 
 |                 fdp = s.Position; | 
 |             } | 
 |             catch (Exception) { } | 
 |  | 
 |             try | 
 |             { | 
 |                 // Use fileLength for progress updates, and to decide whether we can | 
 |                 // skip encryption and compression altogether (in case of length==zero) | 
 |                 long fileLength = SetInputAndFigureFileLength(ref input); | 
 |  | 
 |                 // Wrap a counting stream around the raw output stream: | 
 |                 // This is the last thing that happens before the bits go to the | 
 |                 // application-provided stream. | 
 |                 // | 
 |                 // Sometimes s is a CountingStream. Doesn't matter. Wrap it with a | 
 |                 // counter anyway. We need to count at both levels. | 
 |  | 
 |                 CountingStream entryCounter = new CountingStream(s); | 
 |  | 
 |                 Stream encryptor; | 
 |                 Stream compressor; | 
 |  | 
 |                 if (fileLength != 0L) | 
 |                 { | 
 |                     // Maybe wrap an encrypting stream around the counter: This will | 
 |                     // happen BEFORE output counting, and AFTER compression, if encryption | 
 |                     // is used. | 
 |                     encryptor = MaybeApplyEncryption(entryCounter); | 
 |  | 
 |                     // Maybe wrap a compressing Stream around that. | 
 |                     // This will happen BEFORE encryption (if any) as we write data out. | 
 |                     compressor = MaybeApplyCompression(encryptor, fileLength); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     encryptor = compressor = entryCounter; | 
 |                 } | 
 |  | 
 |                 // Wrap a CrcCalculatorStream around that. | 
 |                 // This will happen BEFORE compression (if any) as we write data out. | 
 |                 var output = new Ionic.Crc.CrcCalculatorStream(compressor, true); | 
 |  | 
 |                 // output.Write() causes this flow: | 
 |                 // calc-crc -> compress -> encrypt -> count -> actually write | 
 |  | 
 |                 if (this._Source == ZipEntrySource.WriteDelegate) | 
 |                 { | 
 |                     // allow the application to write the data | 
 |                     this._WriteDelegate(this.FileName, output); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // synchronously copy the input stream to the output stream-chain | 
 |                     byte[] buffer = new byte[BufferSize]; | 
 |                     int n; | 
 |                     while ((n = SharedUtilities.ReadWithRetry(input, buffer, 0, buffer.Length, FileName)) != 0) | 
 |                     { | 
 |                         output.Write(buffer, 0, n); | 
 |                         OnWriteBlock(output.TotalBytesSlurped, fileLength); | 
 |                         if (_ioOperationCanceled) | 
 |                             break; | 
 |                     } | 
 |                 } | 
 |  | 
 |                 FinishOutputStream(s, entryCounter, encryptor, compressor, output); | 
 |             } | 
 |             finally | 
 |             { | 
 |                 if (this._Source == ZipEntrySource.JitStream) | 
 |                 { | 
 |                     // allow the application to close the stream | 
 |                     if (this._CloseDelegate != null) | 
 |                         this._CloseDelegate(this.FileName, input); | 
 |                 } | 
 |                 else if ((input as FileStream) != null) | 
 |                 { | 
 | #if NETCF | 
 |                     input.Close(); | 
 | #else | 
 |                     input.Dispose(); | 
 | #endif | 
 |                 } | 
 |             } | 
 |  | 
 |             if (_ioOperationCanceled) | 
 |                 return; | 
 |  | 
 |             // set FDP now, to allow for re-streaming | 
 |             this.__FileDataPosition = fdp; | 
 |             PostProcessOutput(s); | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Set the input stream and get its length, if possible.  The length is | 
 |         ///   used for progress updates, AND, to allow an optimization in case of | 
 |         ///   a stream/file of zero length. In that case we skip the Encrypt and | 
 |         ///   compression Stream. (like DeflateStream or BZip2OutputStream) | 
 |         /// </summary> | 
 |         private long SetInputAndFigureFileLength(ref Stream input) | 
 |         { | 
 |             long fileLength = -1L; | 
 |             // get the original stream: | 
 |             if (this._Source == ZipEntrySource.Stream) | 
 |             { | 
 |                 PrepSourceStream(); | 
 |                 input = this._sourceStream; | 
 |  | 
 |                 // Try to get the length, no big deal if not available. | 
 |                 try { fileLength = this._sourceStream.Length; } | 
 |                 catch (NotSupportedException) { } | 
 |             } | 
 |             else if (this._Source == ZipEntrySource.ZipFile) | 
 |             { | 
 |                 // we are "re-streaming" the zip entry. | 
 |                 string pwd = (_Encryption_FromZipFile == EncryptionAlgorithm.None) ? null : (this._Password ?? this._container.Password); | 
 |                 this._sourceStream = InternalOpenReader(pwd); | 
 |                 PrepSourceStream(); | 
 |                 input = this._sourceStream; | 
 |                 fileLength = this._sourceStream.Length; | 
 |             } | 
 |             else if (this._Source == ZipEntrySource.JitStream) | 
 |             { | 
 |                 // allow the application to open the stream | 
 |                 if (this._sourceStream == null) _sourceStream = this._OpenDelegate(this.FileName); | 
 |                 PrepSourceStream(); | 
 |                 input = this._sourceStream; | 
 |                 try { fileLength = this._sourceStream.Length; } | 
 |                 catch (NotSupportedException) { } | 
 |             } | 
 |             else if (this._Source == ZipEntrySource.FileSystem) | 
 |             { | 
 |                 // workitem 7145 | 
 |                 FileShare fs = FileShare.ReadWrite; | 
 | #if !NETCF | 
 |                 // FileShare.Delete is not defined for the Compact Framework | 
 |                 fs |= FileShare.Delete; | 
 | #endif | 
 |                 // workitem 8423 | 
 |                 input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, fs); | 
 |                 fileLength = input.Length; | 
 |             } | 
 |  | 
 |             return fileLength; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         internal void FinishOutputStream(Stream s, | 
 |                                          CountingStream entryCounter, | 
 |                                          Stream encryptor, | 
 |                                          Stream compressor, | 
 |                                          Ionic.Crc.CrcCalculatorStream output) | 
 |         { | 
 |             if (output == null) return; | 
 |  | 
 |             output.Close(); | 
 |  | 
 |             // by calling Close() on the deflate stream, we write the footer bytes, as necessary. | 
 |             if ((compressor as Ionic.Zlib.DeflateStream) != null) | 
 |                 compressor.Close(); | 
 | #if BZIP | 
 |             else if ((compressor as Ionic.BZip2.BZip2OutputStream) != null) | 
 |                 compressor.Close(); | 
 | #if !NETCF | 
 |             else if ((compressor as Ionic.BZip2.ParallelBZip2OutputStream) != null) | 
 |                 compressor.Close(); | 
 | #endif | 
 | #endif | 
 |  | 
 | #if !NETCF | 
 |             else if ((compressor as Ionic.Zlib.ParallelDeflateOutputStream) != null) | 
 |                 compressor.Close(); | 
 | #endif | 
 |  | 
 |             encryptor.Flush(); | 
 |             encryptor.Close(); | 
 |  | 
 |             _LengthOfTrailer = 0; | 
 |  | 
 |             _UncompressedSize = output.TotalBytesSlurped; | 
 |  | 
 | #if AESCRYPTO | 
 |             WinZipAesCipherStream wzacs = encryptor as WinZipAesCipherStream; | 
 |             if (wzacs != null && _UncompressedSize > 0) | 
 |             { | 
 |                 s.Write(wzacs.FinalAuthentication, 0, 10); | 
 |                 _LengthOfTrailer += 10; | 
 |             } | 
 | #endif | 
 |             _CompressedFileDataSize = entryCounter.BytesWritten; | 
 |             _CompressedSize = _CompressedFileDataSize;   // may be adjusted | 
 |             _Crc32 = output.Crc; | 
 |  | 
 |             // Set _RelativeOffsetOfLocalHeader now, to allow for re-streaming | 
 |             StoreRelativeOffset(); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         internal void PostProcessOutput(Stream s) | 
 |         { | 
 |             var s1 = s as CountingStream; | 
 |  | 
 |             // workitem 8931 - for WriteDelegate. | 
 |             // The WriteDelegate changes things because there can be a zero-byte stream | 
 |             // written. In all other cases DotNetZip knows the length of the stream | 
 |             // before compressing and encrypting. In this case we have to circle back, | 
 |             // and omit all the crypto stuff - the GP bitfield, and the crypto header. | 
 |             if (_UncompressedSize == 0 && _CompressedSize == 0) | 
 |             { | 
 |                 if (this._Source == ZipEntrySource.ZipOutputStream) return;  // nothing to do... | 
 |  | 
 |                 if (_Password != null) | 
 |                 { | 
 |                     int headerBytesToRetract = 0; | 
 |                     if (Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |                         headerBytesToRetract = 12; | 
 | #if AESCRYPTO | 
 |                     else if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                              Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |                     { | 
 |                         headerBytesToRetract = _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length; | 
 |                     } | 
 | #endif | 
 |                     if (this._Source == ZipEntrySource.ZipOutputStream && !s.CanSeek) | 
 |                         throw new ZipException("Zero bytes written, encryption in use, and non-seekable output."); | 
 |  | 
 |                     if (Encryption != EncryptionAlgorithm.None) | 
 |                     { | 
 |                         // seek back in the stream to un-output the security metadata | 
 |                         s.Seek(-1 * headerBytesToRetract, SeekOrigin.Current); | 
 |                         s.SetLength(s.Position); | 
 |                         // workitem 10178 | 
 |                         Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |  | 
 |                         // workitem 11131 | 
 |                         // adjust the count on the CountingStream as necessary | 
 |                         if (s1 != null) s1.Adjust(headerBytesToRetract); | 
 |  | 
 |                         // subtract the size of the security header from the _LengthOfHeader | 
 |                         _LengthOfHeader -= headerBytesToRetract; | 
 |                         __FileDataPosition -= headerBytesToRetract; | 
 |                     } | 
 |                     _Password = null; | 
 |  | 
 |                     // turn off the encryption bit | 
 |                     _BitField &= ~(0x0001); | 
 |  | 
 |                     // copy the updated bitfield value into the header | 
 |                     int j = 6; | 
 |                     _EntryHeader[j++] = (byte)(_BitField & 0x00FF); | 
 |                     _EntryHeader[j++] = (byte)((_BitField & 0xFF00) >> 8); | 
 |  | 
 | #if AESCRYPTO | 
 |                     if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                         Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |                     { | 
 |                         // Fix the extra field - overwrite the 0x9901 headerId | 
 |                         // with dummy data. (arbitrarily, 0x9999) | 
 |                         Int16 fnLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256); | 
 |                         int offx = 30 + fnLength; | 
 |                         int aesIndex = FindExtraFieldSegment(_EntryHeader, offx, 0x9901); | 
 |                         if (aesIndex >= 0) | 
 |                         { | 
 |                             _EntryHeader[aesIndex++] = 0x99; | 
 |                             _EntryHeader[aesIndex++] = 0x99; | 
 |                         } | 
 |                     } | 
 | #endif | 
 |                 } | 
 |  | 
 |                 CompressionMethod = 0; | 
 |                 Encryption = EncryptionAlgorithm.None; | 
 |             } | 
 |             else if (_zipCrypto_forWrite != null | 
 | #if AESCRYPTO | 
 |                      || _aesCrypto_forWrite != null | 
 | #endif | 
 |                      ) | 
 |  | 
 |             { | 
 |                 if (Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |                 { | 
 |                     _CompressedSize += 12; // 12 extra bytes for the encryption header | 
 |                 } | 
 | #if AESCRYPTO | 
 |                 else if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                          Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |                 { | 
 |                     // adjust the compressed size to include the variable (salt+pv) | 
 |                     // security header and 10-byte trailer. According to the winzip AES | 
 |                     // spec, that metadata is included in the "Compressed Size" figure | 
 |                     // when encoding the zip archive. | 
 |                     _CompressedSize += _aesCrypto_forWrite.SizeOfEncryptionMetadata; | 
 |                 } | 
 | #endif | 
 |             } | 
 |  | 
 |             int i = 8; | 
 |             _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF); | 
 |             _EntryHeader[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); | 
 |  | 
 |             i = 14; | 
 |             // CRC - the correct value now | 
 |             _EntryHeader[i++] = (byte)(_Crc32 & 0x000000FF); | 
 |             _EntryHeader[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); | 
 |             _EntryHeader[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); | 
 |             _EntryHeader[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); | 
 |  | 
 |             SetZip64Flags(); | 
 |  | 
 |             // (i==26) filename length (Int16) | 
 |             Int16 filenameLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256); | 
 |             Int16 extraFieldLength = (short)(_EntryHeader[28] + _EntryHeader[29] * 256); | 
 |  | 
 |             if (_OutputUsesZip64.Value) | 
 |             { | 
 |                 // VersionNeededToExtract - set to 45 to indicate zip64 | 
 |                 _EntryHeader[4] = (byte)(45 & 0x00FF); | 
 |                 _EntryHeader[5] = 0x00; | 
 |  | 
 |                 // workitem 7924 - don't need bit 3 | 
 |                 // // workitem 7917 | 
 |                 // // set bit 3 for ZIP64 compatibility with WinZip12 | 
 |                 // _BitField |= 0x0008; | 
 |                 // _EntryHeader[6] = (byte)(_BitField & 0x00FF); | 
 |  | 
 |                 // CompressedSize and UncompressedSize - 0xFF | 
 |                 for (int j = 0; j < 8; j++) | 
 |                     _EntryHeader[i++] = 0xff; | 
 |  | 
 |                 // At this point we need to find the "Extra field" that follows the | 
 |                 // filename.  We had already emitted it, but the data (uncomp, comp, | 
 |                 // ROLH) was not available at the time we did so.  Here, we emit it | 
 |                 // again, with final values. | 
 |  | 
 |                 i = 30 + filenameLength; | 
 |                 _EntryHeader[i++] = 0x01;  // zip64 | 
 |                 _EntryHeader[i++] = 0x00; | 
 |  | 
 |                 i += 2; // skip over data size, which is 16+4 | 
 |  | 
 |                 Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, _EntryHeader, i, 8); | 
 |                 i += 8; | 
 |                 Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, _EntryHeader, i, 8); | 
 |             } | 
 |             else | 
 |             { | 
 |                 // VersionNeededToExtract - reset to 20 since no zip64 | 
 |                 _EntryHeader[4] = (byte)(20 & 0x00FF); | 
 |                 _EntryHeader[5] = 0x00; | 
 |  | 
 |                 // CompressedSize - the correct value now | 
 |                 i = 18; | 
 |                 _EntryHeader[i++] = (byte)(_CompressedSize & 0x000000FF); | 
 |                 _EntryHeader[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); | 
 |                 _EntryHeader[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); | 
 |                 _EntryHeader[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); | 
 |  | 
 |                 // UncompressedSize - the correct value now | 
 |                 _EntryHeader[i++] = (byte)(_UncompressedSize & 0x000000FF); | 
 |                 _EntryHeader[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); | 
 |                 _EntryHeader[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); | 
 |                 _EntryHeader[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); | 
 |  | 
 |                 // The HeaderId in the extra field header, is already dummied out. | 
 |                 if (extraFieldLength != 0) | 
 |                 { | 
 |                     i = 30 + filenameLength; | 
 |                     // For zip archives written by this library, if the zip64 | 
 |                     // header exists, it is the first header. Because of the logic | 
 |                     // used when first writing the _EntryHeader bytes, the | 
 |                     // HeaderId is not guaranteed to be any particular value.  So | 
 |                     // we determine if the first header is a putative zip64 header | 
 |                     // by examining the datasize.  UInt16 HeaderId = | 
 |                     // (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256); | 
 |                     Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256); | 
 |                     if (DataSize == 16) | 
 |                     { | 
 |                         // reset to Header Id to dummy value, effectively dummy-ing out the zip64 metadata | 
 |                         _EntryHeader[i++] = 0x99; | 
 |                         _EntryHeader[i++] = 0x99; | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |  | 
 | #if AESCRYPTO | 
 |  | 
 |             if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                 Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 // Must set compressionmethod to 0x0063 (decimal 99) | 
 |                 // | 
 |                 // and then set the compression method bytes inside the extra | 
 |                 // field to the actual compression method value. | 
 |  | 
 |                 i = 8; | 
 |                 _EntryHeader[i++] = 0x63; | 
 |                 _EntryHeader[i++] = 0; | 
 |  | 
 |                 i = 30 + filenameLength; | 
 |                 do | 
 |                 { | 
 |                     UInt16 HeaderId = (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256); | 
 |                     Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256); | 
 |                     if (HeaderId != 0x9901) | 
 |                     { | 
 |                         // skip this header | 
 |                         i += DataSize + 4; | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         i += 9; | 
 |                         // actual compression method | 
 |                         _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF); | 
 |                         _EntryHeader[i++] = (byte)(_CompressionMethod & 0xFF00); | 
 |                     } | 
 |                 } while (i < (extraFieldLength - 30 - filenameLength)); | 
 |             } | 
 | #endif | 
 |  | 
 |             // finally, write the data. | 
 |  | 
 |             // workitem 7216 - sometimes we don't seek even if we CAN.  ASP.NET | 
 |             // Response.OutputStream, or stdout are non-seekable.  But we may also want | 
 |             // to NOT seek in other cases, eg zip64.  For all cases, we just check bit 3 | 
 |             // to see if we want to seek.  There's one exception - if using a | 
 |             // ZipOutputStream, and PKZip encryption is in use, then we set bit 3 even | 
 |             // if the out is seekable. This is so the check on the last byte of the | 
 |             // PKZip Encryption Header can be done on the current time, as opposed to | 
 |             // the CRC, to prevent streaming the file twice.  So, test for | 
 |             // ZipOutputStream and seekable, and if so, seek back, even if bit 3 is set. | 
 |  | 
 |             if ((_BitField & 0x0008) != 0x0008 || | 
 |                  (this._Source == ZipEntrySource.ZipOutputStream && s.CanSeek)) | 
 |             { | 
 |                 // seek back and rewrite the entry header | 
 |                 var zss = s as ZipSegmentedStream; | 
 |                 if (zss != null && _diskNumber != zss.CurrentSegment) | 
 |                 { | 
 |                     // In this case the entry header is in a different file, | 
 |                     // which has already been closed. Need to re-open it. | 
 |                     using (Stream hseg = ZipSegmentedStream.ForUpdate(this._container.ZipFile.Name, _diskNumber)) | 
 |                     { | 
 |                         hseg.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); | 
 |                         hseg.Write(_EntryHeader, 0, _EntryHeader.Length); | 
 |                     } | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // seek in the raw output stream, to the beginning of the header for | 
 |                     // this entry. | 
 |                     // workitem 8098: ok (output) | 
 |                     s.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); | 
 |  | 
 |                     // write the updated header to the output stream | 
 |                     s.Write(_EntryHeader, 0, _EntryHeader.Length); | 
 |  | 
 |                     // adjust the count on the CountingStream as necessary | 
 |                     if (s1 != null) s1.Adjust(_EntryHeader.Length); | 
 |  | 
 |                     // seek in the raw output stream, to the end of the file data | 
 |                     // for this entry | 
 |                     s.Seek(_CompressedSize, SeekOrigin.Current); | 
 |                 } | 
 |             } | 
 |  | 
 |             // emit the descriptor - only if not a directory. | 
 |             if (((_BitField & 0x0008) == 0x0008) && !IsDirectory) | 
 |             { | 
 |                 byte[] Descriptor = new byte[16 + (_OutputUsesZip64.Value ? 8 : 0)]; | 
 |                 i = 0; | 
 |  | 
 |                 // signature | 
 |                 Array.Copy(BitConverter.GetBytes(ZipConstants.ZipEntryDataDescriptorSignature), 0, Descriptor, i, 4); | 
 |                 i += 4; | 
 |  | 
 |                 // CRC - the correct value now | 
 |                 Array.Copy(BitConverter.GetBytes(_Crc32), 0, Descriptor, i, 4); | 
 |                 i += 4; | 
 |  | 
 |                 // workitem 7917 | 
 |                 if (_OutputUsesZip64.Value) | 
 |                 { | 
 |                     // CompressedSize - the correct value now | 
 |                     Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, Descriptor, i, 8); | 
 |                     i += 8; | 
 |  | 
 |                     // UncompressedSize - the correct value now | 
 |                     Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, Descriptor, i, 8); | 
 |                     i += 8; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // CompressedSize - (lower 32 bits) the correct value now | 
 |                     Descriptor[i++] = (byte)(_CompressedSize & 0x000000FF); | 
 |                     Descriptor[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); | 
 |                     Descriptor[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); | 
 |                     Descriptor[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); | 
 |  | 
 |                     // UncompressedSize - (lower 32 bits) the correct value now | 
 |                     Descriptor[i++] = (byte)(_UncompressedSize & 0x000000FF); | 
 |                     Descriptor[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); | 
 |                     Descriptor[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); | 
 |                     Descriptor[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); | 
 |                 } | 
 |  | 
 |                 // finally, write the trailing descriptor to the output stream | 
 |                 s.Write(Descriptor, 0, Descriptor.Length); | 
 |  | 
 |                 _LengthOfTrailer += Descriptor.Length; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void SetZip64Flags() | 
 |         { | 
 |             // zip64 housekeeping | 
 |             _entryRequiresZip64 = new Nullable<bool> | 
 |                 (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF); | 
 |  | 
 |             // validate the ZIP64 usage | 
 |             if (_container.Zip64 == Zip64Option.Never && _entryRequiresZip64.Value) | 
 |                 throw new ZipException("Compressed or Uncompressed size, or offset exceeds the maximum value. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); | 
 |  | 
 |             _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Prepare the given stream for output - wrap it in a CountingStream, and | 
 |         ///   then in a CRC stream, and an encryptor and deflator as appropriate. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     Previously this was used in ZipEntry.Write(), but in an effort to | 
 |         ///     introduce some efficiencies in that method I've refactored to put the | 
 |         ///     code inline.  This method still gets called by ZipOutputStream. | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         internal void PrepOutputStream(Stream s, | 
 |                                        long streamLength, | 
 |                                        out CountingStream outputCounter, | 
 |                                        out Stream encryptor, | 
 |                                        out Stream compressor, | 
 |                                        out Ionic.Crc.CrcCalculatorStream output) | 
 |         { | 
 |             TraceWriteLine("PrepOutputStream: e({0}) comp({1}) crypto({2}) zf({3})", | 
 |                            FileName, | 
 |                            CompressionLevel, | 
 |                            Encryption, | 
 |                            _container.Name); | 
 |  | 
 |             // Wrap a counting stream around the raw output stream: | 
 |             // This is the last thing that happens before the bits go to the | 
 |             // application-provided stream. | 
 |             outputCounter = new CountingStream(s); | 
 |  | 
 |             // Sometimes the incoming "raw" output stream is already a CountingStream. | 
 |             // Doesn't matter. Wrap it with a counter anyway. We need to count at both | 
 |             // levels. | 
 |  | 
 |             if (streamLength != 0L) | 
 |             { | 
 |                 // Maybe wrap an encrypting stream around that: | 
 |                 // This will happen BEFORE output counting, and AFTER deflation, if encryption | 
 |                 // is used. | 
 |                 encryptor = MaybeApplyEncryption(outputCounter); | 
 |  | 
 |                 // Maybe wrap a compressing Stream around that. | 
 |                 // This will happen BEFORE encryption (if any) as we write data out. | 
 |                 compressor = MaybeApplyCompression(encryptor, streamLength); | 
 |             } | 
 |             else | 
 |             { | 
 |                 encryptor = compressor = outputCounter; | 
 |             } | 
 |             // Wrap a CrcCalculatorStream around that. | 
 |             // This will happen BEFORE compression (if any) as we write data out. | 
 |             output = new Ionic.Crc.CrcCalculatorStream(compressor, true); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private Stream MaybeApplyCompression(Stream s, long streamLength) | 
 |         { | 
 |             if (_CompressionMethod == 0x08 && CompressionLevel != Ionic.Zlib.CompressionLevel.None) | 
 |             { | 
 | #if !NETCF | 
 |                 // ParallelDeflateThreshold == 0    means ALWAYS use parallel deflate | 
 |                 // ParallelDeflateThreshold == -1L  means NEVER use parallel deflate | 
 |                 // Other values specify the actual threshold. | 
 |                 if (_container.ParallelDeflateThreshold == 0L || | 
 |                     (streamLength > _container.ParallelDeflateThreshold && | 
 |                      _container.ParallelDeflateThreshold > 0L)) | 
 |                 { | 
 |                     // This is sort of hacky. | 
 |                     // | 
 |                     // It's expensive to create a ParallelDeflateOutputStream, because | 
 |                     // of the large memory buffers.  But the class is unlike most Stream | 
 |                     // classes in that it can be re-used, so the caller can compress | 
 |                     // multiple files with it, one file at a time.  The key is to call | 
 |                     // Reset() on it, in between uses. | 
 |                     // | 
 |                     // The ParallelDeflateOutputStream is attached to the container | 
 |                     // itself - there is just one for the entire ZipFile or | 
 |                     // ZipOutputStream. So it gets created once, per save, and then | 
 |                     // re-used many times. | 
 |                     // | 
 |                     // This approach will break when we go to a "parallel save" | 
 |                     // approach, where multiple entries within the zip file are being | 
 |                     // compressed and saved at the same time.  But for now it's ok. | 
 |                     // | 
 |  | 
 |                     // instantiate the ParallelDeflateOutputStream | 
 |                     if (_container.ParallelDeflater == null) | 
 |                     { | 
 |                         _container.ParallelDeflater = | 
 |                             new ParallelDeflateOutputStream(s, | 
 |                                                                         CompressionLevel, | 
 |                                                                        _container.Strategy, | 
 |                                                                        true); | 
 |                         // can set the codec buffer size only before the first call to Write(). | 
 |                         if (_container.CodecBufferSize > 0) | 
 |                             _container.ParallelDeflater.BufferSize = _container.CodecBufferSize; | 
 |                         if (_container.ParallelDeflateMaxBufferPairs > 0) | 
 |                             _container.ParallelDeflater.MaxBufferPairs = | 
 |                                 _container.ParallelDeflateMaxBufferPairs; | 
 |                     } | 
 |                     // reset it with the new stream | 
 |                     Ionic.Zlib.ParallelDeflateOutputStream o1 = _container.ParallelDeflater; | 
 |                     o1.Reset(s); | 
 |                     return o1; | 
 |                 } | 
 | #endif | 
 |                 var o = new DeflateStream(s, OfficeOpenXml.Packaging.Ionic.Zlib.CompressionMode.Compress, | 
 |                                                      CompressionLevel, | 
 |                                                      true); | 
 |                 if (_container.CodecBufferSize > 0) | 
 |                     o.BufferSize = _container.CodecBufferSize; | 
 |                 o.Strategy = _container.Strategy; | 
 |                 return o; | 
 |             } | 
 |  | 
 |  | 
 | #if BZIP | 
 |             if (_CompressionMethod == 0x0c) | 
 |             { | 
 | #if !NETCF | 
 |                 if (_container.ParallelDeflateThreshold == 0L || | 
 |                     (streamLength > _container.ParallelDeflateThreshold && | 
 |                      _container.ParallelDeflateThreshold > 0L)) | 
 |                 { | 
 |  | 
 |                     var o1 = new Ionic.BZip2.ParallelBZip2OutputStream(s, true); | 
 |                     return o1; | 
 |                 } | 
 | #endif | 
 |                 var o = new Ionic.BZip2.BZip2OutputStream(s, true); | 
 |                 return o; | 
 |             } | 
 | #endif | 
 |  | 
 |             return s; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private Stream MaybeApplyEncryption(Stream s) | 
 |         { | 
 |             if (Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |             { | 
 |                 TraceWriteLine("MaybeApplyEncryption: e({0}) PKZIP", FileName); | 
 |  | 
 |                 return new ZipCipherStream(s, _zipCrypto_forWrite, CryptoMode.Encrypt); | 
 |             } | 
 | #if AESCRYPTO | 
 |             if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                      Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 TraceWriteLine("MaybeApplyEncryption: e({0}) AES", FileName); | 
 |  | 
 |                 return new WinZipAesCipherStream(s, _aesCrypto_forWrite, CryptoMode.Encrypt); | 
 |             } | 
 | #endif | 
 |             TraceWriteLine("MaybeApplyEncryption: e({0}) None", FileName); | 
 |  | 
 |             return s; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void OnZipErrorWhileSaving(Exception e) | 
 |         { | 
 |             if (_container.ZipFile != null) | 
 |                 _ioOperationCanceled = _container.ZipFile.OnZipErrorSaving(this, e); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         internal void Write(Stream s) | 
 |         { | 
 |             var cs1 = s as CountingStream; | 
 |             var zss1 = s as ZipSegmentedStream; | 
 |  | 
 |             bool done = false; | 
 |             do | 
 |             { | 
 |                 try | 
 |                 { | 
 |                     // When the app is updating a zip file, it may be possible to | 
 |                     // just copy data for a ZipEntry from the source zipfile to the | 
 |                     // destination, as a block, without decompressing and | 
 |                     // recompressing, etc.  But, in some cases the app modifies the | 
 |                     // properties on a ZipEntry prior to calling Save(). A change to | 
 |                     // any of the metadata - the FileName, CompressioLeve and so on, | 
 |                     // means DotNetZip cannot simply copy through the existing | 
 |                     // ZipEntry data unchanged. | 
 |                     // | 
 |                     // There are two cases: | 
 |                     // | 
 |                     //  1. Changes to only metadata, which means the header and | 
 |                     //     central directory must be changed. | 
 |                     // | 
 |                     //  2. Changes to the properties that affect the compressed | 
 |                     //     stream, such as CompressionMethod, CompressionLevel, or | 
 |                     //     EncryptionAlgorithm. In this case, DotNetZip must | 
 |                     //     "re-stream" the data: the old entry data must be maybe | 
 |                     //     decrypted, maybe decompressed, then maybe re-compressed | 
 |                     //     and maybe re-encrypted. | 
 |                     // | 
 |                     // This test checks if the source for the entry data is a zip file, and | 
 |                     // if a restream is necessary.  If NOT, then it just copies through | 
 |                     // one entry, potentially changing the metadata. | 
 |  | 
 |                     if (_Source == ZipEntrySource.ZipFile && !_restreamRequiredOnSave) | 
 |                     { | 
 |                         CopyThroughOneEntry(s); | 
 |                         return; | 
 |                     } | 
 |  | 
 |                     // Is the entry a directory?  If so, the write is relatively simple. | 
 |                     if (IsDirectory) | 
 |                     { | 
 |                         WriteHeader(s, 1); | 
 |                         StoreRelativeOffset(); | 
 |                         _entryRequiresZip64 = new Nullable<bool>(_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF); | 
 |                         _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); | 
 |                         // handle case for split archives | 
 |                         if (zss1 != null) | 
 |                             _diskNumber = zss1.CurrentSegment; | 
 |  | 
 |                         return; | 
 |                     } | 
 |  | 
 |                     // At this point, the source for this entry is not a directory, and | 
 |                     // not a previously created zip file, or the source for the entry IS | 
 |                     // a previously created zip but the settings whave changed in | 
 |                     // important ways and therefore we will need to process the | 
 |                     // bytestream (compute crc, maybe compress, maybe encrypt) in order | 
 |                     // to write the content into the new zip. | 
 |                     // | 
 |                     // We do this in potentially 2 passes: The first time we do it as | 
 |                     // requested, maybe with compression and maybe encryption.  If that | 
 |                     // causes the bytestream to inflate in size, and if compression was | 
 |                     // on, then we turn off compression and do it again. | 
 |  | 
 |  | 
 |                     bool readAgain = true; | 
 |                     int nCycles = 0; | 
 |                     do | 
 |                     { | 
 |                         nCycles++; | 
 |  | 
 |                         WriteHeader(s, nCycles); | 
 |  | 
 |                         // write the encrypted header | 
 |                         WriteSecurityMetadata(s); | 
 |  | 
 |                         // write the (potentially compressed, potentially encrypted) file data | 
 |                         _WriteEntryData(s); | 
 |  | 
 |                         // track total entry size (including the trailing descriptor and MAC) | 
 |                         _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer; | 
 |  | 
 |                         // The file data has now been written to the stream, and | 
 |                         // the file pointer is positioned directly after file data. | 
 |  | 
 |                         if (nCycles > 1) readAgain = false; | 
 |                         else if (!s.CanSeek) readAgain = false; | 
 |                         else readAgain = WantReadAgain(); | 
 |  | 
 |                         if (readAgain) | 
 |                         { | 
 |                             // Seek back in the raw output stream, to the beginning of the file | 
 |                             // data for this entry. | 
 |  | 
 |                             // handle case for split archives | 
 |                             if (zss1 != null) | 
 |                             { | 
 |                                 // Console.WriteLine("***_diskNumber/first: {0}", _diskNumber); | 
 |                                 // Console.WriteLine("***_diskNumber/current: {0}", zss.CurrentSegment); | 
 |                                 zss1.TruncateBackward(_diskNumber, _RelativeOffsetOfLocalHeader); | 
 |                             } | 
 |                             else | 
 |                                 // workitem 8098: ok (output). | 
 |                                 s.Seek(_RelativeOffsetOfLocalHeader, SeekOrigin.Begin); | 
 |  | 
 |                             // If the last entry expands, we read again; but here, we must | 
 |                             // truncate the stream to prevent garbage data after the | 
 |                             // end-of-central-directory. | 
 |  | 
 |                             // workitem 8098: ok (output). | 
 |                             s.SetLength(s.Position); | 
 |  | 
 |                             // Adjust the count on the CountingStream as necessary. | 
 |                             if (cs1 != null) cs1.Adjust(_TotalEntrySize); | 
 |                         } | 
 |                     } | 
 |                     while (readAgain); | 
 |                     _skippedDuringSave = false; | 
 |                     done = true; | 
 |                 } | 
 |                 catch (System.Exception exc1) | 
 |                 { | 
 |                     ZipErrorAction orig = this.ZipErrorAction; | 
 |                     int loop = 0; | 
 |                     do | 
 |                     { | 
 |                         if (ZipErrorAction == ZipErrorAction.Throw) | 
 |                             throw; | 
 |  | 
 |                         if (ZipErrorAction == ZipErrorAction.Skip || | 
 |                             ZipErrorAction == ZipErrorAction.Retry) | 
 |                         { | 
 |                             // must reset file pointer here. | 
 |                             // workitem 13903 - seek back only when necessary | 
 |                             long p1 = (cs1 != null) | 
 |                                 ? cs1.ComputedPosition | 
 |                                 : s.Position; | 
 |                             long delta = p1 - _future_ROLH; | 
 |                             if (delta > 0) | 
 |                             { | 
 |                                 s.Seek(delta, SeekOrigin.Current); // may throw | 
 |                                 long p2 = s.Position; | 
 |                                 s.SetLength(s.Position);  // to prevent garbage if this is the last entry | 
 |                                 if (cs1 != null) cs1.Adjust(p1 - p2); | 
 |                             } | 
 |                             if (ZipErrorAction == ZipErrorAction.Skip) | 
 |                             { | 
 |                                 WriteStatus("Skipping file {0} (exception: {1})", LocalFileName, exc1.ToString()); | 
 |  | 
 |                                 _skippedDuringSave = true; | 
 |                                 done = true; | 
 |                             } | 
 |                             else | 
 |                                 this.ZipErrorAction = orig; | 
 |                             break; | 
 |                         } | 
 |  | 
 |                         if (loop > 0) throw; | 
 |  | 
 |                         if (ZipErrorAction == ZipErrorAction.InvokeErrorEvent) | 
 |                         { | 
 |                             OnZipErrorWhileSaving(exc1); | 
 |                             if (_ioOperationCanceled) | 
 |                             { | 
 |                                 done = true; | 
 |                                 break; | 
 |                             } | 
 |                         } | 
 |                         loop++; | 
 |                     } | 
 |                     while (true); | 
 |                 } | 
 |             } | 
 |             while (!done); | 
 |         } | 
 |  | 
 |  | 
 |         internal void StoreRelativeOffset() | 
 |         { | 
 |             _RelativeOffsetOfLocalHeader = _future_ROLH; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         internal void NotifySaveComplete() | 
 |         { | 
 |             // When updating a zip file, there are two contexts for properties | 
 |             // like Encryption or CompressionMethod - the values read from the | 
 |             // original zip file, and the values used in the updated zip file. | 
 |             // The _FromZipFile versions are the originals.  At the end of a save, | 
 |             // these values are the same.  So we need to update them.  This takes | 
 |             // care of the boundary case where a single zipfile instance can be | 
 |             // saved multiple times, with distinct changes to the properties on | 
 |             // the entries, in between each Save(). | 
 |             _Encryption_FromZipFile = _Encryption; | 
 |             _CompressionMethod_FromZipFile = _CompressionMethod; | 
 |             _restreamRequiredOnSave = false; | 
 |             _metadataChanged = false; | 
 |             //_Source = ZipEntrySource.None; | 
 |             _Source = ZipEntrySource.ZipFile; // workitem 10694 | 
 |         } | 
 |  | 
 |  | 
 |         internal void WriteSecurityMetadata(Stream outstream) | 
 |         { | 
 |             if (Encryption == EncryptionAlgorithm.None) | 
 |                 return; | 
 |  | 
 |             string pwd = this._Password; | 
 |  | 
 |             // special handling for source == ZipFile. | 
 |             // Want to support the case where we re-stream an encrypted entry. This will involve, | 
 |             // at runtime, reading, decrypting, and decompressing from the original zip file, then | 
 |             // compressing, encrypting, and writing to the output zip file. | 
 |  | 
 |             // If that's what we're doing, and the password hasn't been set on the entry, | 
 |             // we use the container (ZipFile/ZipOutputStream) password to decrypt. | 
 |             // This test here says to use the container password to re-encrypt, as well, | 
 |             // with that password, if the entry password is null. | 
 |  | 
 |             if (this._Source == ZipEntrySource.ZipFile && pwd == null) | 
 |                 pwd = this._container.Password; | 
 |  | 
 |             if (pwd == null) | 
 |             { | 
 |                 _zipCrypto_forWrite = null; | 
 | #if AESCRYPTO | 
 |                 _aesCrypto_forWrite = null; | 
 | #endif | 
 |                 return; | 
 |             } | 
 |  | 
 |             TraceWriteLine("WriteSecurityMetadata: e({0}) crypto({1}) pw({2})", | 
 |                            FileName, Encryption.ToString(), pwd); | 
 |  | 
 |             if (Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |             { | 
 |                 // If PKZip (weak) encryption is in use, then the encrypted entry data | 
 |                 // is preceded by 12-byte "encryption header" for the entry. | 
 |  | 
 |                 _zipCrypto_forWrite = ZipCrypto.ForWrite(pwd); | 
 |  | 
 |                 // generate the random 12-byte header: | 
 |                 var rnd = new System.Random(); | 
 |                 byte[] encryptionHeader = new byte[12]; | 
 |                 rnd.NextBytes(encryptionHeader); | 
 |  | 
 |                 // workitem 8271 | 
 |                 if ((this._BitField & 0x0008) == 0x0008) | 
 |                 { | 
 |                     // In the case that bit 3 of the general purpose bit flag is set to | 
 |                     // indicate the presence of a 'data descriptor' (signature | 
 |                     // 0x08074b50), the last byte of the decrypted header is sometimes | 
 |                     // compared with the high-order byte of the lastmodified time, | 
 |                     // rather than the high-order byte of the CRC, to verify the | 
 |                     // password. | 
 |                     // | 
 |                     // This is not documented in the PKWare Appnote.txt. | 
 |                     // This was discovered this by analysis of the Crypt.c source file in the | 
 |                     // InfoZip library | 
 |                     // http://www.info-zip.org/pub/infozip/ | 
 |  | 
 |                     // Also, winzip insists on this! | 
 |                     _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified); | 
 |                     encryptionHeader[11] = (byte)((this._TimeBlob >> 8) & 0xff); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // When bit 3 is not set, the CRC value is required before | 
 |                     // encryption of the file data begins. In this case there is no way | 
 |                     // around it: must read the stream in its entirety to compute the | 
 |                     // actual CRC before proceeding. | 
 |                     FigureCrc32(); | 
 |                     encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff); | 
 |                 } | 
 |  | 
 |                 // Encrypt the random header, INCLUDING the final byte which is either | 
 |                 // the high-order byte of the CRC32, or the high-order byte of the | 
 |                 // _TimeBlob.  Must do this BEFORE encrypting the file data.  This | 
 |                 // step changes the state of the cipher, or in the words of the PKZIP | 
 |                 // spec, it "further initializes" the cipher keys. | 
 |  | 
 |                 byte[] cipherText = _zipCrypto_forWrite.EncryptMessage(encryptionHeader, encryptionHeader.Length); | 
 |  | 
 |                 // Write the ciphered bonafide encryption header. | 
 |                 outstream.Write(cipherText, 0, cipherText.Length); | 
 |                 _LengthOfHeader += cipherText.Length;  // 12 bytes | 
 |             } | 
 |  | 
 | #if AESCRYPTO | 
 |             else if (Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                 Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 // If WinZip AES encryption is in use, then the encrypted entry data is | 
 |                 // preceded by a variable-sized Salt and a 2-byte "password | 
 |                 // verification" value for the entry. | 
 |  | 
 |                 int keystrength = GetKeyStrengthInBits(Encryption); | 
 |                 _aesCrypto_forWrite = WinZipAesCrypto.Generate(pwd, keystrength); | 
 |                 outstream.Write(_aesCrypto_forWrite.Salt, 0, _aesCrypto_forWrite._Salt.Length); | 
 |                 outstream.Write(_aesCrypto_forWrite.GeneratedPV, 0, _aesCrypto_forWrite.GeneratedPV.Length); | 
 |                 _LengthOfHeader += _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length; | 
 |  | 
 |                 TraceWriteLine("WriteSecurityMetadata: AES e({0}) keybits({1}) _LOH({2})", | 
 |                                FileName, keystrength, _LengthOfHeader); | 
 |  | 
 |             } | 
 | #endif | 
 |  | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void CopyThroughOneEntry(Stream outStream) | 
 |         { | 
 |             // Just read the entry from the existing input zipfile and write to the output. | 
 |             // But, if metadata has changed (like file times or attributes), or if the ZIP64 | 
 |             // option has changed, we can re-stream the entry data but must recompute the | 
 |             // metadata. | 
 |             if (this.LengthOfHeader == 0) | 
 |                 throw new BadStateException("Bad header length."); | 
 |  | 
 |             // is it necessary to re-constitute new metadata for this entry? | 
 |             bool needRecompute = _metadataChanged || | 
 |                 (this.ArchiveStream is ZipSegmentedStream) || | 
 |                 (outStream is ZipSegmentedStream) || | 
 |                 (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) || | 
 |                 (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always); | 
 |  | 
 |             if (needRecompute) | 
 |                 CopyThroughWithRecompute(outStream); | 
 |             else | 
 |                 CopyThroughWithNoChange(outStream); | 
 |  | 
 |             // zip64 housekeeping | 
 |             _entryRequiresZip64 = new Nullable<bool> | 
 |                 (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || | 
 |                 _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF | 
 |                 ); | 
 |  | 
 |             _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void CopyThroughWithRecompute(Stream outstream) | 
 |         { | 
 |             int n; | 
 |             byte[] bytes = new byte[BufferSize]; | 
 |             var input = new CountingStream(this.ArchiveStream); | 
 |  | 
 |             long origRelativeOffsetOfHeader = _RelativeOffsetOfLocalHeader; | 
 |  | 
 |             // The header length may change due to rename of file, add a comment, etc. | 
 |             // We need to retain the original. | 
 |             int origLengthOfHeader = LengthOfHeader; // including crypto bytes! | 
 |  | 
 |             // WriteHeader() has the side effect of changing _RelativeOffsetOfLocalHeader | 
 |             // and setting _LengthOfHeader.  While ReadHeader() reads the crypto header if | 
 |             // present, WriteHeader() does not write the crypto header. | 
 |             WriteHeader(outstream, 0); | 
 |             StoreRelativeOffset(); | 
 |  | 
 |             if (!this.FileName.EndsWith("/")) | 
 |             { | 
 |                 // Not a directory; there is file data. | 
 |                 // Seek to the beginning of the entry data in the input stream. | 
 |  | 
 |                 long pos = origRelativeOffsetOfHeader + origLengthOfHeader; | 
 |                 int len = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); | 
 |                 pos -= len; // want to keep the crypto header | 
 |                 _LengthOfHeader += len; | 
 |  | 
 |                 input.Seek(pos, SeekOrigin.Begin); | 
 |  | 
 |                 // copy through everything after the header to the output stream | 
 |                 long remaining = this._CompressedSize; | 
 |  | 
 |                 while (remaining > 0) | 
 |                 { | 
 |                     len = (remaining > bytes.Length) ? bytes.Length : (int)remaining; | 
 |  | 
 |                     // read | 
 |                     n = input.Read(bytes, 0, len); | 
 |                     //_CheckRead(n); | 
 |  | 
 |                     // write | 
 |                     outstream.Write(bytes, 0, n); | 
 |                     remaining -= n; | 
 |                     OnWriteBlock(input.BytesRead, this._CompressedSize); | 
 |                     if (_ioOperationCanceled) | 
 |                         break; | 
 |                 } | 
 |  | 
 |                 // bit 3 descriptor | 
 |                 if ((this._BitField & 0x0008) == 0x0008) | 
 |                 { | 
 |                     int size = 16; | 
 |                     if (_InputUsesZip64) size += 8; | 
 |                     byte[] Descriptor = new byte[size]; | 
 |                     input.Read(Descriptor, 0, size); | 
 |  | 
 |                     if (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) | 
 |                     { | 
 |                         // original descriptor was 24 bytes, now we need 16. | 
 |                         // Must check for underflow here. | 
 |                         // signature + CRC. | 
 |                         outstream.Write(Descriptor, 0, 8); | 
 |  | 
 |                         // Compressed | 
 |                         if (_CompressedSize > 0xFFFFFFFF) | 
 |                             throw new InvalidOperationException("ZIP64 is required"); | 
 |                         outstream.Write(Descriptor, 8, 4); | 
 |  | 
 |                         // UnCompressed | 
 |                         if (_UncompressedSize > 0xFFFFFFFF) | 
 |                             throw new InvalidOperationException("ZIP64 is required"); | 
 |                         outstream.Write(Descriptor, 16, 4); | 
 |                         _LengthOfTrailer -= 8; | 
 |                     } | 
 |                     else if (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always) | 
 |                     { | 
 |                         // original descriptor was 16 bytes, now we need 24 | 
 |                         // signature + CRC | 
 |                         byte[] pad = new byte[4]; | 
 |                         outstream.Write(Descriptor, 0, 8); | 
 |                         // Compressed | 
 |                         outstream.Write(Descriptor, 8, 4); | 
 |                         outstream.Write(pad, 0, 4); | 
 |                         // UnCompressed | 
 |                         outstream.Write(Descriptor, 12, 4); | 
 |                         outstream.Write(pad, 0, 4); | 
 |                         _LengthOfTrailer += 8; | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         // same descriptor on input and output. Copy it through. | 
 |                         outstream.Write(Descriptor, 0, size); | 
 |                         //_LengthOfTrailer += size; | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer; | 
 |         } | 
 |  | 
 |  | 
 |         private void CopyThroughWithNoChange(Stream outstream) | 
 |         { | 
 |             int n; | 
 |             byte[] bytes = new byte[BufferSize]; | 
 |             var input = new CountingStream(this.ArchiveStream); | 
 |  | 
 |             // seek to the beginning of the entry data in the input stream | 
 |             input.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); | 
 |  | 
 |             if (this._TotalEntrySize == 0) | 
 |             { | 
 |                 // We've never set the length of the entry. | 
 |                 // Set it here. | 
 |                 this._TotalEntrySize = this._LengthOfHeader + this._CompressedFileDataSize + _LengthOfTrailer; | 
 |  | 
 |                 // The CompressedSize includes all the leading metadata associated | 
 |                 // to encryption, if any, as well as the compressed data, or | 
 |                 // compressed-then-encrypted data, and the trailer in case of AES. | 
 |  | 
 |                 // The CompressedFileData size is the same, less the encryption | 
 |                 // framing data (12 bytes header for PKZip; 10/18 bytes header and | 
 |                 // 10 byte trailer for AES). | 
 |  | 
 |                 // The _LengthOfHeader includes all the zip entry header plus the | 
 |                 // crypto header, if any.  The _LengthOfTrailer includes the | 
 |                 // 10-byte MAC for AES, where appropriate, and the bit-3 | 
 |                 // Descriptor, where applicable. | 
 |             } | 
 |  | 
 |  | 
 |             // workitem 5616 | 
 |             // remember the offset, within the output stream, of this particular entry header. | 
 |             // This may have changed if any of the other entries changed (eg, if a different | 
 |             // entry was removed or added.) | 
 |             var counter = outstream as CountingStream; | 
 |             _RelativeOffsetOfLocalHeader = (counter != null) | 
 |                 ? counter.ComputedPosition | 
 |                 : outstream.Position;  // BytesWritten | 
 |  | 
 |             // copy through the header, filedata, trailer, everything... | 
 |             long remaining = this._TotalEntrySize; | 
 |             while (remaining > 0) | 
 |             { | 
 |                 int len = (remaining > bytes.Length) ? bytes.Length : (int)remaining; | 
 |  | 
 |                 // read | 
 |                 n = input.Read(bytes, 0, len); | 
 |                 //_CheckRead(n); | 
 |  | 
 |                 // write | 
 |                 outstream.Write(bytes, 0, n); | 
 |                 remaining -= n; | 
 |                 OnWriteBlock(input.BytesRead, this._TotalEntrySize); | 
 |                 if (_ioOperationCanceled) | 
 |                     break; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         [System.Diagnostics.ConditionalAttribute("Trace")] | 
 |         private void TraceWriteLine(string format, params object[] varParams) | 
 |         { | 
 |             lock (_outputLock) | 
 |             { | 
 |                 int tid = System.Threading.Thread.CurrentThread.GetHashCode(); | 
 | #if ! (NETCF || SILVERLIGHT) | 
 |                 Console.ForegroundColor = (ConsoleColor)(tid % 8 + 8); | 
 | #endif | 
 |                 Console.Write("{0:000} ZipEntry.Write ", tid); | 
 |                 Console.WriteLine(format, varParams); | 
 | #if ! (NETCF || SILVERLIGHT) | 
 |                 Console.ResetColor(); | 
 | #endif | 
 |             } | 
 |         } | 
 |  | 
 |         private object _outputLock = new Object(); | 
 |     } | 
 | } |