Free cookie consent management tool by TermsFeed Policy Generator

source: brances/2913_MatlabScriptProblemInstanceProvider/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipEntry.Write.cs @ 15887

Last change on this file since 15887 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 113.6 KB
Line 
1//#define Trace
2
3// ZipEntry.Write.cs
4// ------------------------------------------------------------------
5//
6// Copyright (c) 2009-2011 Dino Chiesa
7// All rights reserved.
8//
9// This code module is part of DotNetZip, a zipfile class library.
10//
11// ------------------------------------------------------------------
12//
13// This code is licensed under the Microsoft Public License.
14// See the file License.txt for the license details.
15// More info on: http://dotnetzip.codeplex.com
16//
17// ------------------------------------------------------------------
18//
19// Last Saved: <2011-July-30 14:55:47>
20//
21// ------------------------------------------------------------------
22//
23// This module defines logic for writing (saving) the ZipEntry into a
24// zip file.
25//
26// ------------------------------------------------------------------
27
28
29using OfficeOpenXml.Packaging.Ionic.Zlib;
30using System;
31using System.IO;
32using RE = System.Text.RegularExpressions;
33
34namespace OfficeOpenXml.Packaging.Ionic.Zip
35{
36    internal partial class ZipEntry
37    {
38        internal void WriteCentralDirectoryEntry(Stream s)
39        {
40            byte[] bytes = new byte[4096];
41            int i = 0;
42            // signature
43            bytes[i++] = (byte)(ZipConstants.ZipDirEntrySignature & 0x000000FF);
44            bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x0000FF00) >> 8);
45            bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x00FF0000) >> 16);
46            bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0xFF000000) >> 24);
47
48            // Version Made By
49            // workitem 7071
50            // We must not overwrite the VersionMadeBy field when writing out a zip
51            // archive.  The VersionMadeBy tells the zip reader the meaning of the
52            // File attributes.  Overwriting the VersionMadeBy will result in
53            // inconsistent metadata.  Consider the scenario where the application
54            // opens and reads a zip file that had been created on Linux. Then the
55            // app adds one file to the Zip archive, and saves it.  The file
56            // attributes for all the entries added on Linux will be significant for
57            // Linux.  Therefore the VersionMadeBy for those entries must not be
58            // changed.  Only the entries that are actually created on Windows NTFS
59            // should get the VersionMadeBy indicating Windows/NTFS.
60            bytes[i++] = (byte)(_VersionMadeBy & 0x00FF);
61            bytes[i++] = (byte)((_VersionMadeBy & 0xFF00) >> 8);
62
63            // Apparently we want to duplicate the extra field here; we cannot
64            // simply zero it out and assume tools and apps will use the right one.
65
66            ////Int16 extraFieldLengthSave = (short)(_EntryHeader[28] + _EntryHeader[29] * 256);
67            ////_EntryHeader[28] = 0;
68            ////_EntryHeader[29] = 0;
69
70            // Version Needed, Bitfield, compression method, lastmod,
71            // crc, compressed and uncompressed sizes, filename length and extra field length.
72            // These are all present in the local file header, but they may be zero values there.
73            // So we cannot just copy them.
74
75            // workitem 11969: Version Needed To Extract in central directory must be
76            // the same as the local entry or MS .NET System.IO.Zip fails read.
77            Int16 vNeeded = (Int16)(VersionNeeded != 0 ? VersionNeeded : 20);
78            // workitem 12964
79            if (_OutputUsesZip64==null)
80            {
81                // a zipentry in a zipoutputstream, with zero bytes written
82                _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always);
83            }
84
85            Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : vNeeded);
86#if BZIP
87            if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2)
88                versionNeededToExtract = 46;
89#endif
90
91            bytes[i++] = (byte)(versionNeededToExtract & 0x00FF);
92            bytes[i++] = (byte)((versionNeededToExtract & 0xFF00) >> 8);
93
94            bytes[i++] = (byte)(_BitField & 0x00FF);
95            bytes[i++] = (byte)((_BitField & 0xFF00) >> 8);
96
97            bytes[i++] = (byte)(_CompressionMethod & 0x00FF);
98            bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
99
100#if AESCRYPTO
101            if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
102            Encryption == EncryptionAlgorithm.WinZipAes256)
103            {
104                i -= 2;
105                bytes[i++] = 0x63;
106                bytes[i++] = 0;
107            }
108#endif
109
110            bytes[i++] = (byte)(_TimeBlob & 0x000000FF);
111            bytes[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8);
112            bytes[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16);
113            bytes[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24);
114            bytes[i++] = (byte)(_Crc32 & 0x000000FF);
115            bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
116            bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
117            bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
118
119            int j = 0;
120            if (_OutputUsesZip64.Value)
121            {
122                // CompressedSize (Int32) and UncompressedSize - all 0xFF
123                for (j = 0; j < 8; j++)
124                    bytes[i++] = 0xFF;
125            }
126            else
127            {
128                bytes[i++] = (byte)(_CompressedSize & 0x000000FF);
129                bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
130                bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
131                bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
132
133                bytes[i++] = (byte)(_UncompressedSize & 0x000000FF);
134                bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
135                bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
136                bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
137            }
138
139            byte[] fileNameBytes = GetEncodedFileNameBytes();
140            Int16 filenameLength = (Int16)fileNameBytes.Length;
141            bytes[i++] = (byte)(filenameLength & 0x00FF);
142            bytes[i++] = (byte)((filenameLength & 0xFF00) >> 8);
143
144            // do this again because now we have real data
145            _presumeZip64 = _OutputUsesZip64.Value;
146
147            // workitem 11131
148            //
149            // cannot generate the extra field again, here's why: In the case of a
150            // zero-byte entry, which uses encryption, DotNetZip will "remove" the
151            // encryption from the entry.  It does this in PostProcessOutput; it
152            // modifies the entry header, and rewrites it, resetting the Bitfield
153            // (one bit indicates encryption), and potentially resetting the
154            // compression method - for AES the Compression method is 0x63, and it
155            // would get reset to zero (no compression).  It then calls SetLength()
156            // to truncate the stream to remove the encryption header (12 bytes for
157            // AES256).  But, it leaves the previously-generated "Extra Field"
158            // metadata (11 bytes) for AES in the entry header. This extra field
159            // data is now "orphaned" - it refers to AES encryption when in fact no
160            // AES encryption is used. But no problem, the PKWARE spec says that
161            // unrecognized extra fields can just be ignored. ok.  After "removal"
162            // of AES encryption, the length of the Extra Field can remains the
163            // same; it's just that there will be 11 bytes in there that previously
164            // pertained to AES which are now unused. Even the field code is still
165            // there, but it will be unused by readers, as the encryption bit is not
166            // set.
167            //
168            // Re-calculating the Extra field now would produce a block that is 11
169            // bytes shorter, and that mismatch - between the extra field in the
170            // local header and the extra field in the Central Directory - would
171            // cause problems. (where? why? what problems?)  So we can't do
172            // that. It's all good though, because though the content may have
173            // changed, the length definitely has not. Also, the _EntryHeader
174            // contains the "updated" extra field (after PostProcessOutput) at
175            // offset (30 + filenameLength).
176
177            _Extra = ConstructExtraField(true);
178
179            Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length);
180            bytes[i++] = (byte)(extraFieldLength & 0x00FF);
181            bytes[i++] = (byte)((extraFieldLength & 0xFF00) >> 8);
182
183            // File (entry) Comment Length
184            // the _CommentBytes private field was set during WriteHeader()
185            int commentLength = (_CommentBytes == null) ? 0 : _CommentBytes.Length;
186
187            // the size of our buffer defines the max length of the comment we can write
188            if (commentLength + i > bytes.Length) commentLength = bytes.Length - i;
189            bytes[i++] = (byte)(commentLength & 0x00FF);
190            bytes[i++] = (byte)((commentLength & 0xFF00) >> 8);
191
192            // Disk number start
193            bool segmented = (this._container.ZipFile != null) &&
194                (this._container.ZipFile.MaxOutputSegmentSize != 0);
195            if (segmented) // workitem 13915
196            {
197                // Emit nonzero disknumber only if saving segmented archive.
198                bytes[i++] = (byte)(_diskNumber & 0x00FF);
199                bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8);
200            }
201            else
202            {
203                // If reading a segmneted archive and saving to a regular archive,
204                // ZipEntry._diskNumber will be non-zero but it should be saved as
205                // zero.
206                bytes[i++] = 0;
207                bytes[i++] = 0;
208            }
209
210            // internal file attrs
211            // workitem 7801
212            bytes[i++] = (byte)((_IsText) ? 1 : 0); // lo bit: filetype hint.  0=bin, 1=txt.
213            bytes[i++] = 0;
214
215            // external file attrs
216            // workitem 7071
217            bytes[i++] = (byte)(_ExternalFileAttrs & 0x000000FF);
218            bytes[i++] = (byte)((_ExternalFileAttrs & 0x0000FF00) >> 8);
219            bytes[i++] = (byte)((_ExternalFileAttrs & 0x00FF0000) >> 16);
220            bytes[i++] = (byte)((_ExternalFileAttrs & 0xFF000000) >> 24);
221
222            // workitem 11131
223            // relative offset of local header.
224            //
225            // If necessary to go to 64-bit value, then emit 0xFFFFFFFF,
226            // else write out the value.
227            //
228            // Even if zip64 is required for other reasons - number of the entry
229            // > 65534, or uncompressed size of the entry > MAX_INT32, the ROLH
230            // need not be stored in a 64-bit field .
231            if (_RelativeOffsetOfLocalHeader > 0xFFFFFFFFL) // _OutputUsesZip64.Value
232            {
233                bytes[i++] = 0xFF;
234                bytes[i++] = 0xFF;
235                bytes[i++] = 0xFF;
236                bytes[i++] = 0xFF;
237            }
238            else
239            {
240                bytes[i++] = (byte)(_RelativeOffsetOfLocalHeader & 0x000000FF);
241                bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x0000FF00) >> 8);
242                bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x00FF0000) >> 16);
243                bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0xFF000000) >> 24);
244            }
245
246            // actual filename
247            Buffer.BlockCopy(fileNameBytes, 0, bytes, i, filenameLength);
248            i += filenameLength;
249
250            // "Extra field"
251            if (_Extra != null)
252            {
253                // workitem 11131
254                //
255                // copy from EntryHeader if available - it may have been updated.
256                // if not, copy from Extra. This would be unnecessary if I just
257                // updated the Extra field when updating EntryHeader, in
258                // PostProcessOutput.
259
260                //?? I don't understand why I wouldn't want to just use
261                // the recalculated Extra field. ??
262
263                // byte[] h = _EntryHeader ?? _Extra;
264                // int offx = (h == _EntryHeader) ? 30 + filenameLength : 0;
265                // Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
266                // i += extraFieldLength;
267
268                byte[] h = _Extra;
269                int offx = 0;
270                Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength);
271                i += extraFieldLength;
272            }
273
274            // file (entry) comment
275            if (commentLength != 0)
276            {
277                // now actually write the comment itself into the byte buffer
278                Buffer.BlockCopy(_CommentBytes, 0, bytes, i, commentLength);
279                // for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
280                //     bytes[i + j] = _CommentBytes[j];
281                i += commentLength;
282            }
283
284            s.Write(bytes, 0, i);
285        }
286
287
288#if INFOZIP_UTF8
289        static private bool FileNameIsUtf8(char[] FileNameChars)
290        {
291            bool isUTF8 = false;
292            bool isUnicode = false;
293            for (int j = 0; j < FileNameChars.Length; j++)
294            {
295                byte[] b = System.BitConverter.GetBytes(FileNameChars[j]);
296                isUnicode |= (b.Length != 2);
297                isUnicode |= (b[1] != 0);
298                isUTF8 |= ((b[0] & 0x80) != 0);
299            }
300
301            return isUTF8;
302        }
303#endif
304
305
306        private byte[] ConstructExtraField(bool forCentralDirectory)
307        {
308            var listOfBlocks = new System.Collections.Generic.List<byte[]>();
309            byte[] block;
310
311            // Conditionally emit an extra field with Zip64 information.  If the
312            // Zip64 option is Always, we emit the field, before knowing that it's
313            // necessary.  Later, if it turns out this entry does not need zip64,
314            // we'll set the header ID to rubbish and the data will be ignored.
315            // This results in additional overhead metadata in the zip file, but
316            // it will be small in comparison to the entry data.
317            //
318            // On the other hand if the Zip64 option is AsNecessary and it's NOT
319            // for the central directory, then we do the same thing.  Or, if the
320            // Zip64 option is AsNecessary and it IS for the central directory,
321            // and the entry requires zip64, then emit the header.
322            if (_container.Zip64 == Zip64Option.Always ||
323                (_container.Zip64 == Zip64Option.AsNecessary &&
324                 (!forCentralDirectory || _entryRequiresZip64.Value)))
325            {
326                // add extra field for zip64 here
327                // workitem 7924
328                int sz = 4 + (forCentralDirectory ? 28 : 16);
329                block = new byte[sz];
330                int i = 0;
331
332                if (_presumeZip64 || forCentralDirectory)
333                {
334                    // HeaderId = always use zip64 extensions.
335                    block[i++] = 0x01;
336                    block[i++] = 0x00;
337                }
338                else
339                {
340                    // HeaderId = dummy data now, maybe set to 0x0001 (ZIP64) later.
341                    block[i++] = 0x99;
342                    block[i++] = 0x99;
343                }
344
345                // DataSize
346                block[i++] = (byte)(sz - 4);  // decimal 28 or 16  (workitem 7924)
347                block[i++] = 0x00;
348
349                // The actual metadata - we may or may not have real values yet...
350
351                // uncompressed size
352                Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, block, i, 8);
353                i += 8;
354                // compressed size
355                Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, block, i, 8);
356                i += 8;
357
358                // workitem 7924 - only include this if the "extra" field is for
359                // use in the central directory.  It is unnecessary and not useful
360                // for local header; makes WinZip choke.
361                if (forCentralDirectory)
362                {
363                    // relative offset
364                    Array.Copy(BitConverter.GetBytes(_RelativeOffsetOfLocalHeader), 0, block, i, 8);
365                    i += 8;
366
367                    // starting disk number
368                    Array.Copy(BitConverter.GetBytes(0), 0, block, i, 4);
369                }
370                listOfBlocks.Add(block);
371            }
372
373
374#if AESCRYPTO
375            if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
376                Encryption == EncryptionAlgorithm.WinZipAes256)
377            {
378                block = new byte[4 + 7];
379                int i = 0;
380                // extra field for WinZip AES
381                // header id
382                block[i++] = 0x01;
383                block[i++] = 0x99;
384
385                // data size
386                block[i++] = 0x07;
387                block[i++] = 0x00;
388
389                // vendor number
390                block[i++] = 0x01;  // AE-1 - means "Verify CRC"
391                block[i++] = 0x00;
392
393                // vendor id "AE"
394                block[i++] = 0x41;
395                block[i++] = 0x45;
396
397                // key strength
398                int keystrength = GetKeyStrengthInBits(Encryption);
399                if (keystrength == 128)
400                    block[i] = 1;
401                else if (keystrength == 256)
402                    block[i] = 3;
403                else
404                    block[i] = 0xFF;
405                i++;
406
407                // actual compression method
408                block[i++] = (byte)(_CompressionMethod & 0x00FF);
409                block[i++] = (byte)(_CompressionMethod & 0xFF00);
410
411                listOfBlocks.Add(block);
412            }
413#endif
414
415            if (_ntfsTimesAreSet && _emitNtfsTimes)
416            {
417                block = new byte[32 + 4];
418                // HeaderId   2 bytes    0x000a == NTFS times
419                // Datasize   2 bytes    32
420                // reserved   4 bytes    ?? don't care
421                // timetag    2 bytes    0x0001 == NTFS time
422                // size       2 bytes    24 == 8 bytes each for ctime, mtime, atime
423                // mtime      8 bytes    win32 ticks since win32epoch
424                // atime      8 bytes    win32 ticks since win32epoch
425                // ctime      8 bytes    win32 ticks since win32epoch
426                int i = 0;
427                // extra field for NTFS times
428                // header id
429                block[i++] = 0x0a;
430                block[i++] = 0x00;
431
432                // data size
433                block[i++] = 32;
434                block[i++] = 0;
435
436                i += 4; // reserved
437
438                // time tag
439                block[i++] = 0x01;
440                block[i++] = 0x00;
441
442                // data size (again)
443                block[i++] = 24;
444                block[i++] = 0;
445
446                Int64 z = _Mtime.ToFileTime();
447                Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
448                i += 8;
449                z = _Atime.ToFileTime();
450                Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
451                i += 8;
452                z = _Ctime.ToFileTime();
453                Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8);
454                i += 8;
455
456                listOfBlocks.Add(block);
457            }
458
459            if (_ntfsTimesAreSet && _emitUnixTimes)
460            {
461                int len = 5 + 4;
462                if (!forCentralDirectory) len += 8;
463
464                block = new byte[len];
465                // local form:
466                // --------------
467                // HeaderId   2 bytes    0x5455 == unix timestamp
468                // Datasize   2 bytes    13
469                // flags      1 byte     7 (low three bits all set)
470                // mtime      4 bytes    seconds since unix epoch
471                // atime      4 bytes    seconds since unix epoch
472                // ctime      4 bytes    seconds since unix epoch
473                //
474                // central directory form:
475                //---------------------------------
476                // HeaderId   2 bytes    0x5455 == unix timestamp
477                // Datasize   2 bytes    5
478                // flags      1 byte     7 (low three bits all set)
479                // mtime      4 bytes    seconds since unix epoch
480                //
481                int i = 0;
482                // extra field for "unix" times
483                // header id
484                block[i++] = 0x55;
485                block[i++] = 0x54;
486
487                // data size
488                block[i++] = unchecked((byte)(len - 4));
489                block[i++] = 0;
490
491                // flags
492                block[i++] = 0x07;
493
494                Int32 z = unchecked((int)((_Mtime - _unixEpoch).TotalSeconds));
495                Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
496                i += 4;
497                if (!forCentralDirectory)
498                {
499                    z = unchecked((int)((_Atime - _unixEpoch).TotalSeconds));
500                    Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
501                    i += 4;
502                    z = unchecked((int)((_Ctime - _unixEpoch).TotalSeconds));
503                    Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4);
504                    i += 4;
505                }
506                listOfBlocks.Add(block);
507            }
508
509
510            // inject other blocks here...
511
512
513            // concatenate any blocks we've got:
514            byte[] aggregateBlock = null;
515            if (listOfBlocks.Count > 0)
516            {
517                int totalLength = 0;
518                int i, current = 0;
519                for (i = 0; i < listOfBlocks.Count; i++)
520                    totalLength += listOfBlocks[i].Length;
521                aggregateBlock = new byte[totalLength];
522                for (i = 0; i < listOfBlocks.Count; i++)
523                {
524                    System.Array.Copy(listOfBlocks[i], 0, aggregateBlock, current, listOfBlocks[i].Length);
525                    current += listOfBlocks[i].Length;
526                }
527            }
528
529            return aggregateBlock;
530        }
531
532
533
534        // private System.Text.Encoding GenerateCommentBytes()
535        // {
536        //     var getEncoding = new Func<System.Text.Encoding>({
537        //     switch (AlternateEncodingUsage)
538        //     {
539        //         case ZipOption.Always:
540        //             return AlternateEncoding;
541        //         case ZipOption.Never:
542        //             return ibm437;
543        //     }
544        //     var cb = ibm437.GetBytes(_Comment);
545        //     // need to use this form of GetString() for .NET CF
546        //     string s1 = ibm437.GetString(cb, 0, cb.Length);
547        //     if (s1 == _Comment)
548        //         return ibm437;
549        //     return AlternateEncoding;
550        //     });
551        //
552        //     var encoding = getEncoding();
553        //     _CommentBytes = encoding.GetBytes(_Comment);
554        //     return encoding;
555        // }
556
557
558        private string NormalizeFileName()
559        {
560            // here, we need to flip the backslashes to forward-slashes,
561            // also, we need to trim the \\server\share syntax from any UNC path.
562            // and finally, we need to remove any leading .\
563
564            string SlashFixed = FileName.Replace("\\", "/");
565            string s1 = null;
566            if ((_TrimVolumeFromFullyQualifiedPaths) && (FileName.Length >= 3)
567                && (FileName[1] == ':') && (SlashFixed[2] == '/'))
568            {
569                // trim off volume letter, colon, and slash
570                s1 = SlashFixed.Substring(3);
571            }
572            else if ((FileName.Length >= 4)
573                     && ((SlashFixed[0] == '/') && (SlashFixed[1] == '/')))
574            {
575                int n = SlashFixed.IndexOf('/', 2);
576                if (n == -1)
577                    throw new ArgumentException("The path for that entry appears to be badly formatted");
578                s1 = SlashFixed.Substring(n + 1);
579            }
580            else if ((FileName.Length >= 3)
581                     && ((SlashFixed[0] == '.') && (SlashFixed[1] == '/')))
582            {
583                // trim off dot and slash
584                s1 = SlashFixed.Substring(2);
585            }
586            else
587            {
588                s1 = SlashFixed;
589            }
590            return s1;
591        }
592
593
594        /// <summary>
595        ///   generate and return a byte array that encodes the filename
596        ///   for the entry.
597        /// </summary>
598        /// <remarks>
599        ///   <para>
600        ///     side effects: generate and store into _CommentBytes the
601        ///     byte array for any comment attached to the entry. Also
602        ///     sets _actualEncoding to indicate the actual encoding
603        ///     used. The same encoding is used for both filename and
604        ///     comment.
605        ///   </para>
606        /// </remarks>
607        private byte[] GetEncodedFileNameBytes()
608        {
609            // workitem 6513
610            var s1 = NormalizeFileName();
611
612            switch(AlternateEncodingUsage)
613            {
614                case ZipOption.Always:
615                    if (!(_Comment == null || _Comment.Length == 0))
616                        _CommentBytes = AlternateEncoding.GetBytes(_Comment);
617                    _actualEncoding = AlternateEncoding;
618                    return AlternateEncoding.GetBytes(s1);
619
620                case ZipOption.Never:
621                    if (!(_Comment == null || _Comment.Length == 0))
622                        _CommentBytes = ibm437.GetBytes(_Comment);
623                    _actualEncoding = ibm437;
624                    return ibm437.GetBytes(s1);
625            }
626
627            // arriving here means AlternateEncodingUsage is "AsNecessary"
628
629            // case ZipOption.AsNecessary:
630            // workitem 6513: when writing, use the alternative encoding
631            // only when _actualEncoding is not yet set (it can be set
632            // during Read), and when ibm437 will not do.
633
634            byte[] result = ibm437.GetBytes(s1);
635            // need to use this form of GetString() for .NET CF
636            string s2 = ibm437.GetString(result, 0, result.Length);
637            _CommentBytes = null;
638            if (s2 != s1)
639            {
640                // Encoding the filename with ibm437 does not allow round-trips.
641                // Therefore, use the alternate encoding.  Assume it will work,
642                // no checking of round trips here.
643                result = AlternateEncoding.GetBytes(s1);
644                if (_Comment != null && _Comment.Length != 0)
645                    _CommentBytes = AlternateEncoding.GetBytes(_Comment);
646                _actualEncoding = AlternateEncoding;
647                return result;
648            }
649
650            _actualEncoding = ibm437;
651
652            // Using ibm437, FileName can be encoded without information
653            // loss; now try the Comment.
654
655            // if there is no comment, use ibm437.
656            if (_Comment == null || _Comment.Length == 0)
657                return result;
658
659            // there is a comment. Get the encoded form.
660            byte[] cbytes = ibm437.GetBytes(_Comment);
661            string c2 = ibm437.GetString(cbytes,0,cbytes.Length);
662
663            // Check for round-trip.
664            if (c2 != Comment)
665            {
666                // Comment cannot correctly be encoded with ibm437.  Use
667                // the alternate encoding.
668
669                result = AlternateEncoding.GetBytes(s1);
670                _CommentBytes = AlternateEncoding.GetBytes(_Comment);
671                _actualEncoding = AlternateEncoding;
672                return result;
673            }
674
675            // use IBM437
676            _CommentBytes = cbytes;
677            return result;
678        }
679
680
681
682        private bool WantReadAgain()
683        {
684            if (_UncompressedSize < 0x10) return false;
685            if (_CompressionMethod == 0x00) return false;
686            if (CompressionLevel == OfficeOpenXml.Packaging.Ionic.Zlib.CompressionLevel.None) return false;
687            if (_CompressedSize < _UncompressedSize) return false;
688
689            if (this._Source == ZipEntrySource.Stream && !this._sourceStream.CanSeek) return false;
690
691#if AESCRYPTO
692            if (_aesCrypto_forWrite != null && (CompressedSize - _aesCrypto_forWrite.SizeOfEncryptionMetadata) <= UncompressedSize + 0x10) return false;
693#endif
694
695            if (_zipCrypto_forWrite != null && (CompressedSize - 12) <= UncompressedSize) return false;
696
697            return true;
698        }
699
700
701
702        private void MaybeUnsetCompressionMethodForWriting(int cycle)
703        {
704            // if we've already tried with compression... turn it off this time
705            if (cycle > 1)
706            {
707                _CompressionMethod = 0x0;
708                return;
709            }
710            // compression for directories = 0x00 (No Compression)
711            if (IsDirectory)
712            {
713                _CompressionMethod = 0x0;
714                return;
715            }
716
717            if (this._Source == ZipEntrySource.ZipFile)
718            {
719                return; // do nothing
720            }
721
722            // If __FileDataPosition is zero, then that means we will get the data
723            // from a file or stream.
724
725            // It is never possible to compress a zero-length file, so we check for
726            // this condition.
727
728            if (this._Source == ZipEntrySource.Stream)
729            {
730                // workitem 7742
731                if (_sourceStream != null && _sourceStream.CanSeek)
732                {
733                    // Length prop will throw if CanSeek is false
734                    long fileLength = _sourceStream.Length;
735                    if (fileLength == 0)
736                    {
737                        _CompressionMethod = 0x00;
738                        return;
739                    }
740                }
741            }
742            else if ((this._Source == ZipEntrySource.FileSystem) && (SharedUtilities.GetFileLength(LocalFileName) == 0L))
743            {
744                _CompressionMethod = 0x00;
745                return;
746            }
747
748            // Ok, we're getting the data to be compressed from a
749            // non-zero-length file or stream, or a file or stream of
750            // unknown length, and we presume that it is non-zero.  In
751            // that case we check the callback to see if the app wants
752            // to tell us whether to compress or not.
753            if (SetCompression != null)
754                CompressionLevel = SetCompression(LocalFileName, _FileNameInArchive);
755
756            // finally, set CompressionMethod to None if CompressionLevel is None
757            if (CompressionLevel == (short)Ionic.Zlib.CompressionLevel.None &&
758                CompressionMethod == Ionic.Zip.CompressionMethod.Deflate)
759                _CompressionMethod = 0x00;
760
761            return;
762        }
763
764
765
766        // write the header info for an entry
767        internal void WriteHeader(Stream s, int cycle)
768        {
769            // Must remember the offset, within the output stream, of this particular
770            // entry header.
771            //
772            // This is for 2 reasons:
773            //
774            //  1. so we can determine the RelativeOffsetOfLocalHeader (ROLH) for
775            //     use in the central directory.
776            //  2. so we can seek backward in case there is an error opening or reading
777            //     the file, and the application decides to skip the file. In this case,
778            //     we need to seek backward in the output stream to allow the next entry
779            //     to be added to the zipfile output stream.
780            //
781            // Normally you would just store the offset before writing to the output
782            // stream and be done with it.  But the possibility to use split archives
783            // makes this approach ineffective.  In split archives, each file or segment
784            // is bound to a max size limit, and each local file header must not span a
785            // segment boundary; it must be written contiguously.  If it will fit in the
786            // current segment, then the ROLH is just the current Position in the output
787            // stream.  If it won't fit, then we need a new file (segment) and the ROLH
788            // is zero.
789            //
790            // But we only can know if it is possible to write a header contiguously
791            // after we know the size of the local header, a size that varies with
792            // things like filename length, comments, and extra fields.  We have to
793            // compute the header fully before knowing whether it will fit.
794            //
795            // That takes care of item #1 above.  Now, regarding #2.  If an error occurs
796            // while computing the local header, we want to just seek backward. The
797            // exception handling logic (in the caller of WriteHeader) uses ROLH to
798            // scroll back.
799            //
800            // All this means we have to preserve the starting offset before computing
801            // the header, and also we have to compute the offset later, to handle the
802            // case of split archives.
803
804            var counter = s as CountingStream;
805
806            // workitem 8098: ok (output)
807            // This may change later, for split archives
808
809            // Don't set _RelativeOffsetOfLocalHeader. Instead, set a temp variable.
810            // This allows for re-streaming, where a zip entry might be read from a
811            // zip archive (and maybe decrypted, and maybe decompressed) and then
812            // written to another zip archive, with different settings for
813            // compression method, compression level, or encryption algorithm.
814            _future_ROLH = (counter != null)
815                ? counter.ComputedPosition
816                : s.Position;
817
818            int j = 0, i = 0;
819
820            byte[] block = new byte[30];
821
822            // signature
823            block[i++] = (byte)(ZipConstants.ZipEntrySignature & 0x000000FF);
824            block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x0000FF00) >> 8);
825            block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x00FF0000) >> 16);
826            block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0xFF000000) >> 24);
827
828            // Design notes for ZIP64:
829            //
830            // The specification says that the header must include the Compressed
831            // and Uncompressed sizes, as well as the CRC32 value.  When creating
832            // a zip via streamed processing, these quantities are not known until
833            // after the compression is done.  Thus, a typical way to do it is to
834            // insert zeroes for these quantities, then do the compression, then
835            // seek back to insert the appropriate values, then seek forward to
836            // the end of the file data.
837            //
838            // There is also the option of using bit 3 in the GP bitfield - to
839            // specify that there is a data descriptor after the file data
840            // containing these three quantities.
841            //
842            // This works when the size of the quantities is known, either 32-bits
843            // or 64 bits as with the ZIP64 extensions.
844            //
845            // With Zip64, the 4-byte fields are set to 0xffffffff, and there is a
846            // corresponding data block in the "extra field" that contains the
847            // actual Compressed, uncompressed sizes.  (As well as an additional
848            // field, the "Relative Offset of Local Header")
849            //
850            // The problem is when the app desires to use ZIP64 extensions
851            // optionally, only when necessary.  Suppose the library assumes no
852            // zip64 extensions when writing the header, then after compression
853            // finds that the size of the data requires zip64.  At this point, the
854            // header, already written to the file, won't have the necessary data
855            // block in the "extra field".  The size of the entry header is fixed,
856            // so it is not possible to just "add on" the zip64 data block after
857            // compressing the file.  On the other hand, always using zip64 will
858            // break interoperability with many other systems and apps.
859            //
860            // The approach we take is to insert a 32-byte dummy data block in the
861            // extra field, whenever zip64 is to be used "as necessary". This data
862            // block will get the actual zip64 HeaderId and zip64 metadata if
863            // necessary.  If not necessary, the data block will get a meaningless
864            // HeaderId (0x1111), and will be filled with zeroes.
865            //
866            // When zip64 is actually in use, we also need to set the
867            // VersionNeededToExtract field to 45.
868            //
869            // There is one additional wrinkle: using zip64 as necessary conflicts
870            // with output to non-seekable devices.  The header is emitted and
871            // must indicate whether zip64 is in use, before we know if zip64 is
872            // necessary.  Because there is no seeking, the header can never be
873            // changed.  Therefore, on non-seekable devices,
874            // Zip64Option.AsNecessary is the same as Zip64Option.Always.
875            //
876
877
878            // version needed- see AppNote.txt.
879            //
880            // need v5.1 for PKZIP strong encryption, or v2.0 for no encryption or
881            // for PK encryption, 4.5 for zip64.  We may reset this later, as
882            // necessary or zip64.
883
884            _presumeZip64 = (_container.Zip64 == Zip64Option.Always ||
885                             (_container.Zip64 == Zip64Option.AsNecessary && !s.CanSeek));
886            Int16 VersionNeededToExtract = (Int16)(_presumeZip64 ? 45 : 20);
887#if BZIP
888            if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2)
889                VersionNeededToExtract = 46;
890#endif
891
892            // (i==4)
893            block[i++] = (byte)(VersionNeededToExtract & 0x00FF);
894            block[i++] = (byte)((VersionNeededToExtract & 0xFF00) >> 8);
895
896            // Get byte array. Side effect: sets ActualEncoding.
897            // Must determine encoding before setting the bitfield.
898            // workitem 6513
899            byte[] fileNameBytes = GetEncodedFileNameBytes();
900            Int16 filenameLength = (Int16)fileNameBytes.Length;
901
902            // general purpose bitfield
903            // In the current implementation, this library uses only these bits
904            // in the GP bitfield:
905            //  bit 0 = if set, indicates the entry is encrypted
906            //  bit 3 = if set, indicates the CRC, C and UC sizes follow the file data.
907            //  bit 6 = strong encryption - for pkware's meaning of strong encryption
908            //  bit 11 = UTF-8 encoding is used in the comment and filename
909
910
911            // Here we set or unset the encryption bit.
912            // _BitField may already be set, as with a ZipEntry added into ZipOutputStream, which
913            // has bit 3 always set. We only want to set one bit
914            if (_Encryption == EncryptionAlgorithm.None)
915                _BitField &= ~1;  // encryption bit OFF
916            else
917                _BitField |= 1;   // encryption bit ON
918
919
920            // workitem 7941: WinZip does not the "strong encryption" bit  when using AES.
921            // This "Strong Encryption" is a PKWare Strong encryption thing.
922            //                 _BitField |= 0x0020;
923
924            // set the UTF8 bit if necessary
925#if SILVERLIGHT
926            if (_actualEncoding.WebName == "utf-8")
927#else
928            if (_actualEncoding.CodePage == System.Text.Encoding.UTF8.CodePage)
929#endif
930                _BitField |= 0x0800;
931
932            // The PKZIP spec says that if bit 3 is set (0x0008) in the General
933            // Purpose BitField, then the CRC, Compressed size, and uncompressed
934            // size are written directly after the file data.
935            //
936            // These 3 quantities are normally present in the regular zip entry
937            // header. But, they are not knowable until after the compression is
938            // done. So, in the normal case, we
939            //
940            //  - write the header, using zeros for these quantities
941            //  - compress the data, and incidentally compute these quantities.
942            //  - seek back and write the correct values them into the header.
943            //
944            // This is nice because, while it is more complicated to write the zip
945            // file, it is simpler and less error prone to read the zip file, and
946            // as a result more applications can read zip files produced this way,
947            // with those 3 quantities in the header.
948            //
949            // But if seeking in the output stream is not possible, then we need
950            // to set the appropriate bitfield and emit these quantities after the
951            // compressed file data in the output.
952            //
953            // workitem 7216 - having trouble formatting a zip64 file that is
954            // readable by WinZip.  not sure why!  What I found is that setting
955            // bit 3 and following all the implications, the zip64 file is
956            // readable by WinZip 12. and Perl's IO::Compress::Zip .  Perl takes
957            // an interesting approach - it always sets bit 3 if ZIP64 in use.
958            // DotNetZip now does the same; this gives better compatibility with
959            // WinZip 12.
960
961            if (IsDirectory || cycle == 99)
962            {
963                // (cycle == 99) indicates a zero-length entry written by ZipOutputStream
964
965                _BitField &= ~0x0008;  // unset bit 3 - no "data descriptor" - ever
966                _BitField &= ~0x0001;  // unset bit 1 - no encryption - ever
967                Encryption = EncryptionAlgorithm.None;
968                Password = null;
969            }
970            else if (!s.CanSeek)
971                _BitField |= 0x0008;
972
973#if DONT_GO_THERE
974            else if (this.Encryption == EncryptionAlgorithm.PkzipWeak  &&
975                     this._Source != ZipEntrySource.ZipFile)
976            {
977                // Set bit 3 to avoid the double-read perf issue.
978                //
979                // When PKZIP encryption is used, byte 11 of the encryption header is
980                // used as a consistency check. It is normally set to the MSByte of the
981                // CRC.  But this means the cRC must be known ebfore compression and
982                // encryption, which means the entire stream has to be read twice.  To
983                // avoid that, the high-byte of the time blob (when in DOS format) can
984                // be used for the consistency check (byte 11 in the encryption header).
985                // But this means the entry must have bit 3 set.
986                //
987                // Previously I used a more complex arrangement - using the methods like
988                // FigureCrc32(), PrepOutputStream() and others, in order to manage the
989                // seek-back in the source stream.  Why?  Because bit 3 is not always
990                // friendly with third-party zip tools, like those on the Mac.
991                //
992                // This is why this code is still ifdef'd  out.
993                //
994                // Might consider making this yet another programmable option -
995                // AlwaysUseBit3ForPkzip.  But that's for another day.
996                //
997                _BitField |= 0x0008;
998            }
999#endif
1000
1001            // (i==6)
1002            block[i++] = (byte)(_BitField & 0x00FF);
1003            block[i++] = (byte)((_BitField & 0xFF00) >> 8);
1004
1005            // Here, we want to set values for Compressed Size, Uncompressed Size,
1006            // and CRC.  If we have __FileDataPosition as not -1 (zero is a valid
1007            // FDP), then that means we are reading this zip entry from a zip
1008            // file, and we have good values for those quantities.
1009            //
1010            // If _FileDataPosition is -1, then we are constructing this Entry
1011            // from nothing.  We zero those quantities now, and we will compute
1012            // actual values for the three quantities later, when we do the
1013            // compression, and then seek back to write them into the appropriate
1014            // place in the header.
1015            if (this.__FileDataPosition == -1)
1016            {
1017                //_UncompressedSize = 0; // do not unset - may need this value for restream
1018                // _Crc32 = 0;           // ditto
1019                _CompressedSize = 0;
1020                _crcCalculated = false;
1021            }
1022
1023            // set compression method here
1024            MaybeUnsetCompressionMethodForWriting(cycle);
1025
1026            // (i==8) compression method
1027            block[i++] = (byte)(_CompressionMethod & 0x00FF);
1028            block[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
1029
1030            if (cycle == 99)
1031            {
1032                // (cycle == 99) indicates a zero-length entry written by ZipOutputStream
1033                SetZip64Flags();
1034            }
1035
1036#if AESCRYPTO
1037            else if (Encryption == EncryptionAlgorithm.WinZipAes128 || Encryption == EncryptionAlgorithm.WinZipAes256)
1038            {
1039                i -= 2;
1040                block[i++] = 0x63;
1041                block[i++] = 0;
1042            }
1043#endif
1044
1045            // LastMod
1046            _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified);
1047
1048            // (i==10) time blob
1049            block[i++] = (byte)(_TimeBlob & 0x000000FF);
1050            block[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8);
1051            block[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16);
1052            block[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24);
1053
1054            // (i==14) CRC - if source==filesystem, this is zero now, actual value
1055            // will be calculated later.  if source==archive, this is a bonafide
1056            // value.
1057            block[i++] = (byte)(_Crc32 & 0x000000FF);
1058            block[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
1059            block[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
1060            block[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
1061
1062            if (_presumeZip64)
1063            {
1064                // (i==18) CompressedSize (Int32) and UncompressedSize - all 0xFF for now
1065                for (j = 0; j < 8; j++)
1066                    block[i++] = 0xFF;
1067            }
1068            else
1069            {
1070                // (i==18) CompressedSize (Int32) - this value may or may not be
1071                // bonafide.  if source == filesystem, then it is zero, and we'll
1072                // learn it after we compress.  if source == archive, then it is
1073                // bonafide data.
1074                block[i++] = (byte)(_CompressedSize & 0x000000FF);
1075                block[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
1076                block[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
1077                block[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
1078
1079                // (i==22) UncompressedSize (Int32) - this value may or may not be
1080                // bonafide.
1081                block[i++] = (byte)(_UncompressedSize & 0x000000FF);
1082                block[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
1083                block[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
1084                block[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
1085            }
1086
1087            // (i==26) filename length (Int16)
1088            block[i++] = (byte)(filenameLength & 0x00FF);
1089            block[i++] = (byte)((filenameLength & 0xFF00) >> 8);
1090
1091            _Extra = ConstructExtraField(false);
1092
1093            // (i==28) extra field length (short)
1094            Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length);
1095            block[i++] = (byte)(extraFieldLength & 0x00FF);
1096            block[i++] = (byte)((extraFieldLength & 0xFF00) >> 8);
1097
1098            // workitem 13542
1099            byte[] bytes = new byte[i + filenameLength + extraFieldLength];
1100
1101            // get the fixed portion
1102            Buffer.BlockCopy(block, 0, bytes, 0, i);
1103            //for (j = 0; j < i; j++) bytes[j] = block[j];
1104
1105            // The filename written to the archive.
1106            Buffer.BlockCopy(fileNameBytes, 0, bytes, i, fileNameBytes.Length);
1107            // for (j = 0; j < fileNameBytes.Length; j++)
1108            //     bytes[i + j] = fileNameBytes[j];
1109
1110            i += fileNameBytes.Length;
1111
1112            // "Extra field"
1113            if (_Extra != null)
1114            {
1115                Buffer.BlockCopy(_Extra, 0, bytes, i, _Extra.Length);
1116                // for (j = 0; j < _Extra.Length; j++)
1117                //     bytes[i + j] = _Extra[j];
1118                i += _Extra.Length;
1119            }
1120
1121            _LengthOfHeader = i;
1122
1123            // handle split archives
1124            var zss = s as ZipSegmentedStream;
1125            if (zss != null)
1126            {
1127                zss.ContiguousWrite = true;
1128                UInt32 requiredSegment = zss.ComputeSegment(i);
1129                if (requiredSegment != zss.CurrentSegment)
1130                    _future_ROLH = 0; // rollover!
1131                else
1132                    _future_ROLH = zss.Position;
1133
1134                _diskNumber = requiredSegment;
1135            }
1136
1137            // validate the ZIP64 usage
1138            if (_container.Zip64 == Zip64Option.Never && (uint)_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF)
1139                throw new ZipException("Offset within the zip archive exceeds 0xFFFFFFFF. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
1140
1141
1142            // finally, write the header to the stream
1143            s.Write(bytes, 0, i);
1144
1145            // now that the header is written, we can turn off the contiguous write restriction.
1146            if (zss != null)
1147                zss.ContiguousWrite = false;
1148
1149            // Preserve this header data, we'll use it again later.
1150            // ..when seeking backward, to write again, after we have the Crc, compressed
1151            //   and uncompressed sizes.
1152            // ..and when writing the central directory structure.
1153            _EntryHeader = bytes;
1154        }
1155
1156
1157
1158
1159        private Int32 FigureCrc32()
1160        {
1161            if (_crcCalculated == false)
1162            {
1163                Stream input = null;
1164                // get the original stream:
1165                if (this._Source == ZipEntrySource.WriteDelegate)
1166                {
1167                    var output = new Ionic.Crc.CrcCalculatorStream(Stream.Null);
1168                    // allow the application to write the data
1169                    this._WriteDelegate(this.FileName, output);
1170                    _Crc32 = output.Crc;
1171                }
1172                else if (this._Source == ZipEntrySource.ZipFile)
1173                {
1174                    // nothing to do - the CRC is already set
1175                }
1176                else
1177                {
1178                    if (this._Source == ZipEntrySource.Stream)
1179                    {
1180                        PrepSourceStream();
1181                        input = this._sourceStream;
1182                    }
1183                    else if (this._Source == ZipEntrySource.JitStream)
1184                    {
1185                        // allow the application to open the stream
1186                        if (this._sourceStream == null)
1187                            _sourceStream = this._OpenDelegate(this.FileName);
1188                        PrepSourceStream();
1189                        input = this._sourceStream;
1190                    }
1191                    else if (this._Source == ZipEntrySource.ZipOutputStream)
1192                    {
1193                    }
1194                    else
1195                    {
1196                        //input = File.OpenRead(LocalFileName);
1197                        input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
1198                    }
1199
1200                    var crc32 = new Ionic.Crc.CRC32();
1201                    _Crc32 = crc32.GetCrc32(input);
1202
1203                    if (_sourceStream == null)
1204                    {
1205#if NETCF
1206                        input.Close();
1207#else
1208                        input.Dispose();
1209#endif
1210                    }
1211                }
1212                _crcCalculated = true;
1213            }
1214            return _Crc32;
1215        }
1216
1217
1218        /// <summary>
1219        ///   Stores the position of the entry source stream, or, if the position is
1220        ///   already stored, seeks to that position.
1221        /// </summary>
1222        ///
1223        /// <remarks>
1224        /// <para>
1225        ///   This method is called in prep for reading the source stream.  If PKZIP
1226        ///   encryption is used, then we need to calc the CRC32 before doing the
1227        ///   encryption, because the CRC is used in the 12th byte of the PKZIP
1228        ///   encryption header.  So, we need to be able to seek backward in the source
1229        ///   when saving the ZipEntry. This method is called from the place that
1230        ///   calculates the CRC, and also from the method that does the encryption of
1231        ///   the file data.
1232        /// </para>
1233        ///
1234        /// <para>
1235        ///   The first time through, this method sets the _sourceStreamOriginalPosition
1236        ///   field. Subsequent calls to this method seek to that position.
1237        /// </para>
1238        /// </remarks>
1239        private void PrepSourceStream()
1240        {
1241            if (_sourceStream == null)
1242                throw new ZipException(String.Format("The input stream is null for entry '{0}'.", FileName));
1243
1244            if (this._sourceStreamOriginalPosition != null)
1245            {
1246                // this will happen the 2nd cycle through, if the stream is seekable
1247                this._sourceStream.Position = this._sourceStreamOriginalPosition.Value;
1248            }
1249            else if (this._sourceStream.CanSeek)
1250            {
1251                // this will happen the first cycle through, if seekable
1252                this._sourceStreamOriginalPosition = new Nullable<Int64>(this._sourceStream.Position);
1253            }
1254            else if (this.Encryption == EncryptionAlgorithm.PkzipWeak)
1255            {
1256                // In general, using PKZIP encryption on a a zip entry whose input
1257                // comes from a non-seekable stream, is tricky.  Here's why:
1258                //
1259                // Byte 11 of the PKZIP encryption header is used for password
1260                // validation and consistency checknig.
1261                //
1262                // Normally, the highest byte of the CRC is used as the 11th (last) byte
1263                // in the PKZIP encryption header. This means the CRC must be known
1264                // before encryption is performed. Normally that means we read the full
1265                // data stream, compute the CRC, then seek back and read it again for
1266                // the compression+encryption phase. Obviously this is bad for
1267                // performance with a large input file.
1268                //
1269                // There's a twist in the ZIP spec (actually documented only in infozip
1270                // code, not in the spec itself) that allows the high-order byte of the
1271                // last modified time for the entry, when the lastmod time is in packed
1272                // (DOS) format, to be used for Byte 11 in the encryption header. In
1273                // this case, the bit 3 "data descriptor" must be used.
1274                //
1275                // An intelligent implementation would therefore force the use of the
1276                // bit 3 data descriptor when PKZIP encryption is in use, regardless.
1277                // This avoids the double-read of the stream to be encrypted.  So far,
1278                // DotNetZip doesn't do that; it just punts when the input stream is
1279                // non-seekable, and the output does not use Bit 3.
1280                //
1281                // The other option is to use the CRC when it is already available, eg,
1282                // when the source for the data is a ZipEntry (when the zip file is
1283                // being updated). In this case we already know the CRC and can just use
1284                // what we know.
1285
1286                if (this._Source != ZipEntrySource.ZipFile && ((this._BitField & 0x0008) != 0x0008))
1287                    throw new ZipException("It is not possible to use PKZIP encryption on a non-seekable input stream");
1288            }
1289        }
1290
1291
1292        /// <summary>
1293        /// Copy metadata that may have been changed by the app.  We do this when
1294        /// resetting the zipFile instance.  If the app calls Save() on a ZipFile, then
1295        /// tries to party on that file some more, we may need to Reset() it , which
1296        /// means re-reading the entries and then copying the metadata.  I think.
1297        /// </summary>
1298        internal void CopyMetaData(ZipEntry source)
1299        {
1300            this.__FileDataPosition = source.__FileDataPosition;
1301            this.CompressionMethod = source.CompressionMethod;
1302            this._CompressionMethod_FromZipFile = source._CompressionMethod_FromZipFile;
1303            this._CompressedFileDataSize = source._CompressedFileDataSize;
1304            this._UncompressedSize = source._UncompressedSize;
1305            this._BitField = source._BitField;
1306            this._Source = source._Source;
1307            this._LastModified = source._LastModified;
1308            this._Mtime = source._Mtime;
1309            this._Atime = source._Atime;
1310            this._Ctime = source._Ctime;
1311            this._ntfsTimesAreSet = source._ntfsTimesAreSet;
1312            this._emitUnixTimes = source._emitUnixTimes;
1313            this._emitNtfsTimes = source._emitNtfsTimes;
1314        }
1315
1316
1317        private void OnWriteBlock(Int64 bytesXferred, Int64 totalBytesToXfer)
1318        {
1319            if (_container.ZipFile != null)
1320                _ioOperationCanceled = _container.ZipFile.OnSaveBlock(this, bytesXferred, totalBytesToXfer);
1321        }
1322
1323
1324
1325        private void _WriteEntryData(Stream s)
1326        {
1327            // Read in the data from the input stream (often a file in the filesystem),
1328            // and write it to the output stream, calculating a CRC on it as we go.
1329            // We will also compress and encrypt as necessary.
1330
1331            Stream input = null;
1332            long fdp = -1L;
1333            try
1334            {
1335                // Want to record the position in the zip file of the zip entry
1336                // data (as opposed to the metadata).  s.Position may fail on some
1337                // write-only streams, eg stdout or System.Web.HttpResponseStream.
1338                // We swallow that exception, because we don't care, in that case.
1339                // But, don't set __FileDataPosition directly.  It may be needed
1340                // to READ the zip entry from the zip file, if this is a
1341                // "re-stream" situation. In other words if the zip entry has
1342                // changed compression level, or compression method, or (maybe?)
1343                // encryption algorithm.  In that case if the original entry is
1344                // encrypted, we need __FileDataPosition to be the value for the
1345                // input zip file.  This s.Position is for the output zipfile.  So
1346                // we copy fdp to __FileDataPosition after this entry has been
1347                // (maybe) restreamed.
1348                fdp = s.Position;
1349            }
1350            catch (Exception) { }
1351
1352            try
1353            {
1354                // Use fileLength for progress updates, and to decide whether we can
1355                // skip encryption and compression altogether (in case of length==zero)
1356                long fileLength = SetInputAndFigureFileLength(ref input);
1357
1358                // Wrap a counting stream around the raw output stream:
1359                // This is the last thing that happens before the bits go to the
1360                // application-provided stream.
1361                //
1362                // Sometimes s is a CountingStream. Doesn't matter. Wrap it with a
1363                // counter anyway. We need to count at both levels.
1364
1365                CountingStream entryCounter = new CountingStream(s);
1366
1367                Stream encryptor;
1368                Stream compressor;
1369
1370                if (fileLength != 0L)
1371                {
1372                    // Maybe wrap an encrypting stream around the counter: This will
1373                    // happen BEFORE output counting, and AFTER compression, if encryption
1374                    // is used.
1375                    encryptor = MaybeApplyEncryption(entryCounter);
1376
1377                    // Maybe wrap a compressing Stream around that.
1378                    // This will happen BEFORE encryption (if any) as we write data out.
1379                    compressor = MaybeApplyCompression(encryptor, fileLength);
1380                }
1381                else
1382                {
1383                    encryptor = compressor = entryCounter;
1384                }
1385
1386                // Wrap a CrcCalculatorStream around that.
1387                // This will happen BEFORE compression (if any) as we write data out.
1388                var output = new Ionic.Crc.CrcCalculatorStream(compressor, true);
1389
1390                // output.Write() causes this flow:
1391                // calc-crc -> compress -> encrypt -> count -> actually write
1392
1393                if (this._Source == ZipEntrySource.WriteDelegate)
1394                {
1395                    // allow the application to write the data
1396                    this._WriteDelegate(this.FileName, output);
1397                }
1398                else
1399                {
1400                    // synchronously copy the input stream to the output stream-chain
1401                    byte[] buffer = new byte[BufferSize];
1402                    int n;
1403                    while ((n = SharedUtilities.ReadWithRetry(input, buffer, 0, buffer.Length, FileName)) != 0)
1404                    {
1405                        output.Write(buffer, 0, n);
1406                        OnWriteBlock(output.TotalBytesSlurped, fileLength);
1407                        if (_ioOperationCanceled)
1408                            break;
1409                    }
1410                }
1411
1412                FinishOutputStream(s, entryCounter, encryptor, compressor, output);
1413            }
1414            finally
1415            {
1416                if (this._Source == ZipEntrySource.JitStream)
1417                {
1418                    // allow the application to close the stream
1419                    if (this._CloseDelegate != null)
1420                        this._CloseDelegate(this.FileName, input);
1421                }
1422                else if ((input as FileStream) != null)
1423                {
1424#if NETCF
1425                    input.Close();
1426#else
1427                    input.Dispose();
1428#endif
1429                }
1430            }
1431
1432            if (_ioOperationCanceled)
1433                return;
1434
1435            // set FDP now, to allow for re-streaming
1436            this.__FileDataPosition = fdp;
1437            PostProcessOutput(s);
1438        }
1439
1440
1441        /// <summary>
1442        ///   Set the input stream and get its length, if possible.  The length is
1443        ///   used for progress updates, AND, to allow an optimization in case of
1444        ///   a stream/file of zero length. In that case we skip the Encrypt and
1445        ///   compression Stream. (like DeflateStream or BZip2OutputStream)
1446        /// </summary>
1447        private long SetInputAndFigureFileLength(ref Stream input)
1448        {
1449            long fileLength = -1L;
1450            // get the original stream:
1451            if (this._Source == ZipEntrySource.Stream)
1452            {
1453                PrepSourceStream();
1454                input = this._sourceStream;
1455
1456                // Try to get the length, no big deal if not available.
1457                try { fileLength = this._sourceStream.Length; }
1458                catch (NotSupportedException) { }
1459            }
1460            else if (this._Source == ZipEntrySource.ZipFile)
1461            {
1462                // we are "re-streaming" the zip entry.
1463                string pwd = (_Encryption_FromZipFile == EncryptionAlgorithm.None) ? null : (this._Password ?? this._container.Password);
1464                this._sourceStream = InternalOpenReader(pwd);
1465                PrepSourceStream();
1466                input = this._sourceStream;
1467                fileLength = this._sourceStream.Length;
1468            }
1469            else if (this._Source == ZipEntrySource.JitStream)
1470            {
1471                // allow the application to open the stream
1472                if (this._sourceStream == null) _sourceStream = this._OpenDelegate(this.FileName);
1473                PrepSourceStream();
1474                input = this._sourceStream;
1475                try { fileLength = this._sourceStream.Length; }
1476                catch (NotSupportedException) { }
1477            }
1478            else if (this._Source == ZipEntrySource.FileSystem)
1479            {
1480                // workitem 7145
1481                FileShare fs = FileShare.ReadWrite;
1482#if !NETCF
1483                // FileShare.Delete is not defined for the Compact Framework
1484                fs |= FileShare.Delete;
1485#endif
1486                // workitem 8423
1487                input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, fs);
1488                fileLength = input.Length;
1489            }
1490
1491            return fileLength;
1492        }
1493
1494
1495
1496        internal void FinishOutputStream(Stream s,
1497                                         CountingStream entryCounter,
1498                                         Stream encryptor,
1499                                         Stream compressor,
1500                                         Ionic.Crc.CrcCalculatorStream output)
1501        {
1502            if (output == null) return;
1503
1504            output.Close();
1505
1506            // by calling Close() on the deflate stream, we write the footer bytes, as necessary.
1507            if ((compressor as Ionic.Zlib.DeflateStream) != null)
1508                compressor.Close();
1509#if BZIP
1510            else if ((compressor as Ionic.BZip2.BZip2OutputStream) != null)
1511                compressor.Close();
1512#if !NETCF
1513            else if ((compressor as Ionic.BZip2.ParallelBZip2OutputStream) != null)
1514                compressor.Close();
1515#endif
1516#endif
1517
1518#if !NETCF
1519            else if ((compressor as Ionic.Zlib.ParallelDeflateOutputStream) != null)
1520                compressor.Close();
1521#endif
1522
1523            encryptor.Flush();
1524            encryptor.Close();
1525
1526            _LengthOfTrailer = 0;
1527
1528            _UncompressedSize = output.TotalBytesSlurped;
1529
1530#if AESCRYPTO
1531            WinZipAesCipherStream wzacs = encryptor as WinZipAesCipherStream;
1532            if (wzacs != null && _UncompressedSize > 0)
1533            {
1534                s.Write(wzacs.FinalAuthentication, 0, 10);
1535                _LengthOfTrailer += 10;
1536            }
1537#endif
1538            _CompressedFileDataSize = entryCounter.BytesWritten;
1539            _CompressedSize = _CompressedFileDataSize;   // may be adjusted
1540            _Crc32 = output.Crc;
1541
1542            // Set _RelativeOffsetOfLocalHeader now, to allow for re-streaming
1543            StoreRelativeOffset();
1544        }
1545
1546
1547
1548
1549        internal void PostProcessOutput(Stream s)
1550        {
1551            var s1 = s as CountingStream;
1552
1553            // workitem 8931 - for WriteDelegate.
1554            // The WriteDelegate changes things because there can be a zero-byte stream
1555            // written. In all other cases DotNetZip knows the length of the stream
1556            // before compressing and encrypting. In this case we have to circle back,
1557            // and omit all the crypto stuff - the GP bitfield, and the crypto header.
1558            if (_UncompressedSize == 0 && _CompressedSize == 0)
1559            {
1560                if (this._Source == ZipEntrySource.ZipOutputStream) return;  // nothing to do...
1561
1562                if (_Password != null)
1563                {
1564                    int headerBytesToRetract = 0;
1565                    if (Encryption == EncryptionAlgorithm.PkzipWeak)
1566                        headerBytesToRetract = 12;
1567#if AESCRYPTO
1568                    else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
1569                             Encryption == EncryptionAlgorithm.WinZipAes256)
1570                    {
1571                        headerBytesToRetract = _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length;
1572                    }
1573#endif
1574                    if (this._Source == ZipEntrySource.ZipOutputStream && !s.CanSeek)
1575                        throw new ZipException("Zero bytes written, encryption in use, and non-seekable output.");
1576
1577                    if (Encryption != EncryptionAlgorithm.None)
1578                    {
1579                        // seek back in the stream to un-output the security metadata
1580                        s.Seek(-1 * headerBytesToRetract, SeekOrigin.Current);
1581                        s.SetLength(s.Position);
1582                        // workitem 10178
1583                        Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
1584
1585                        // workitem 11131
1586                        // adjust the count on the CountingStream as necessary
1587                        if (s1 != null) s1.Adjust(headerBytesToRetract);
1588
1589                        // subtract the size of the security header from the _LengthOfHeader
1590                        _LengthOfHeader -= headerBytesToRetract;
1591                        __FileDataPosition -= headerBytesToRetract;
1592                    }
1593                    _Password = null;
1594
1595                    // turn off the encryption bit
1596                    _BitField &= ~(0x0001);
1597
1598                    // copy the updated bitfield value into the header
1599                    int j = 6;
1600                    _EntryHeader[j++] = (byte)(_BitField & 0x00FF);
1601                    _EntryHeader[j++] = (byte)((_BitField & 0xFF00) >> 8);
1602
1603#if AESCRYPTO
1604                    if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
1605                        Encryption == EncryptionAlgorithm.WinZipAes256)
1606                    {
1607                        // Fix the extra field - overwrite the 0x9901 headerId
1608                        // with dummy data. (arbitrarily, 0x9999)
1609                        Int16 fnLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256);
1610                        int offx = 30 + fnLength;
1611                        int aesIndex = FindExtraFieldSegment(_EntryHeader, offx, 0x9901);
1612                        if (aesIndex >= 0)
1613                        {
1614                            _EntryHeader[aesIndex++] = 0x99;
1615                            _EntryHeader[aesIndex++] = 0x99;
1616                        }
1617                    }
1618#endif
1619                }
1620
1621                CompressionMethod = 0;
1622                Encryption = EncryptionAlgorithm.None;
1623            }
1624            else if (_zipCrypto_forWrite != null
1625#if AESCRYPTO
1626                     || _aesCrypto_forWrite != null
1627#endif
1628                     )
1629
1630            {
1631                if (Encryption == EncryptionAlgorithm.PkzipWeak)
1632                {
1633                    _CompressedSize += 12; // 12 extra bytes for the encryption header
1634                }
1635#if AESCRYPTO
1636                else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
1637                         Encryption == EncryptionAlgorithm.WinZipAes256)
1638                {
1639                    // adjust the compressed size to include the variable (salt+pv)
1640                    // security header and 10-byte trailer. According to the winzip AES
1641                    // spec, that metadata is included in the "Compressed Size" figure
1642                    // when encoding the zip archive.
1643                    _CompressedSize += _aesCrypto_forWrite.SizeOfEncryptionMetadata;
1644                }
1645#endif
1646            }
1647
1648            int i = 8;
1649            _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF);
1650            _EntryHeader[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8);
1651
1652            i = 14;
1653            // CRC - the correct value now
1654            _EntryHeader[i++] = (byte)(_Crc32 & 0x000000FF);
1655            _EntryHeader[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8);
1656            _EntryHeader[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16);
1657            _EntryHeader[i++] = (byte)((_Crc32 & 0xFF000000) >> 24);
1658
1659            SetZip64Flags();
1660
1661            // (i==26) filename length (Int16)
1662            Int16 filenameLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256);
1663            Int16 extraFieldLength = (short)(_EntryHeader[28] + _EntryHeader[29] * 256);
1664
1665            if (_OutputUsesZip64.Value)
1666            {
1667                // VersionNeededToExtract - set to 45 to indicate zip64
1668                _EntryHeader[4] = (byte)(45 & 0x00FF);
1669                _EntryHeader[5] = 0x00;
1670
1671                // workitem 7924 - don't need bit 3
1672                // // workitem 7917
1673                // // set bit 3 for ZIP64 compatibility with WinZip12
1674                // _BitField |= 0x0008;
1675                // _EntryHeader[6] = (byte)(_BitField & 0x00FF);
1676
1677                // CompressedSize and UncompressedSize - 0xFF
1678                for (int j = 0; j < 8; j++)
1679                    _EntryHeader[i++] = 0xff;
1680
1681                // At this point we need to find the "Extra field" that follows the
1682                // filename.  We had already emitted it, but the data (uncomp, comp,
1683                // ROLH) was not available at the time we did so.  Here, we emit it
1684                // again, with final values.
1685
1686                i = 30 + filenameLength;
1687                _EntryHeader[i++] = 0x01;  // zip64
1688                _EntryHeader[i++] = 0x00;
1689
1690                i += 2; // skip over data size, which is 16+4
1691
1692                Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, _EntryHeader, i, 8);
1693                i += 8;
1694                Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, _EntryHeader, i, 8);
1695            }
1696            else
1697            {
1698                // VersionNeededToExtract - reset to 20 since no zip64
1699                _EntryHeader[4] = (byte)(20 & 0x00FF);
1700                _EntryHeader[5] = 0x00;
1701
1702                // CompressedSize - the correct value now
1703                i = 18;
1704                _EntryHeader[i++] = (byte)(_CompressedSize & 0x000000FF);
1705                _EntryHeader[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
1706                _EntryHeader[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
1707                _EntryHeader[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
1708
1709                // UncompressedSize - the correct value now
1710                _EntryHeader[i++] = (byte)(_UncompressedSize & 0x000000FF);
1711                _EntryHeader[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
1712                _EntryHeader[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
1713                _EntryHeader[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
1714
1715                // The HeaderId in the extra field header, is already dummied out.
1716                if (extraFieldLength != 0)
1717                {
1718                    i = 30 + filenameLength;
1719                    // For zip archives written by this library, if the zip64
1720                    // header exists, it is the first header. Because of the logic
1721                    // used when first writing the _EntryHeader bytes, the
1722                    // HeaderId is not guaranteed to be any particular value.  So
1723                    // we determine if the first header is a putative zip64 header
1724                    // by examining the datasize.  UInt16 HeaderId =
1725                    // (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256);
1726                    Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256);
1727                    if (DataSize == 16)
1728                    {
1729                        // reset to Header Id to dummy value, effectively dummy-ing out the zip64 metadata
1730                        _EntryHeader[i++] = 0x99;
1731                        _EntryHeader[i++] = 0x99;
1732                    }
1733                }
1734            }
1735
1736
1737#if AESCRYPTO
1738
1739            if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
1740                Encryption == EncryptionAlgorithm.WinZipAes256)
1741            {
1742                // Must set compressionmethod to 0x0063 (decimal 99)
1743                //
1744                // and then set the compression method bytes inside the extra
1745                // field to the actual compression method value.
1746
1747                i = 8;
1748                _EntryHeader[i++] = 0x63;
1749                _EntryHeader[i++] = 0;
1750
1751                i = 30 + filenameLength;
1752                do
1753                {
1754                    UInt16 HeaderId = (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256);
1755                    Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256);
1756                    if (HeaderId != 0x9901)
1757                    {
1758                        // skip this header
1759                        i += DataSize + 4;
1760                    }
1761                    else
1762                    {
1763                        i += 9;
1764                        // actual compression method
1765                        _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF);
1766                        _EntryHeader[i++] = (byte)(_CompressionMethod & 0xFF00);
1767                    }
1768                } while (i < (extraFieldLength - 30 - filenameLength));
1769            }
1770#endif
1771
1772            // finally, write the data.
1773
1774            // workitem 7216 - sometimes we don't seek even if we CAN.  ASP.NET
1775            // Response.OutputStream, or stdout are non-seekable.  But we may also want
1776            // to NOT seek in other cases, eg zip64.  For all cases, we just check bit 3
1777            // to see if we want to seek.  There's one exception - if using a
1778            // ZipOutputStream, and PKZip encryption is in use, then we set bit 3 even
1779            // if the out is seekable. This is so the check on the last byte of the
1780            // PKZip Encryption Header can be done on the current time, as opposed to
1781            // the CRC, to prevent streaming the file twice.  So, test for
1782            // ZipOutputStream and seekable, and if so, seek back, even if bit 3 is set.
1783
1784            if ((_BitField & 0x0008) != 0x0008 ||
1785                 (this._Source == ZipEntrySource.ZipOutputStream && s.CanSeek))
1786            {
1787                // seek back and rewrite the entry header
1788                var zss = s as ZipSegmentedStream;
1789                if (zss != null && _diskNumber != zss.CurrentSegment)
1790                {
1791                    // In this case the entry header is in a different file,
1792                    // which has already been closed. Need to re-open it.
1793                    using (Stream hseg = ZipSegmentedStream.ForUpdate(this._container.ZipFile.Name, _diskNumber))
1794                    {
1795                        hseg.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
1796                        hseg.Write(_EntryHeader, 0, _EntryHeader.Length);
1797                    }
1798                }
1799                else
1800                {
1801                    // seek in the raw output stream, to the beginning of the header for
1802                    // this entry.
1803                    // workitem 8098: ok (output)
1804                    s.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
1805
1806                    // write the updated header to the output stream
1807                    s.Write(_EntryHeader, 0, _EntryHeader.Length);
1808
1809                    // adjust the count on the CountingStream as necessary
1810                    if (s1 != null) s1.Adjust(_EntryHeader.Length);
1811
1812                    // seek in the raw output stream, to the end of the file data
1813                    // for this entry
1814                    s.Seek(_CompressedSize, SeekOrigin.Current);
1815                }
1816            }
1817
1818            // emit the descriptor - only if not a directory.
1819            if (((_BitField & 0x0008) == 0x0008) && !IsDirectory)
1820            {
1821                byte[] Descriptor = new byte[16 + (_OutputUsesZip64.Value ? 8 : 0)];
1822                i = 0;
1823
1824                // signature
1825                Array.Copy(BitConverter.GetBytes(ZipConstants.ZipEntryDataDescriptorSignature), 0, Descriptor, i, 4);
1826                i += 4;
1827
1828                // CRC - the correct value now
1829                Array.Copy(BitConverter.GetBytes(_Crc32), 0, Descriptor, i, 4);
1830                i += 4;
1831
1832                // workitem 7917
1833                if (_OutputUsesZip64.Value)
1834                {
1835                    // CompressedSize - the correct value now
1836                    Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, Descriptor, i, 8);
1837                    i += 8;
1838
1839                    // UncompressedSize - the correct value now
1840                    Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, Descriptor, i, 8);
1841                    i += 8;
1842                }
1843                else
1844                {
1845                    // CompressedSize - (lower 32 bits) the correct value now
1846                    Descriptor[i++] = (byte)(_CompressedSize & 0x000000FF);
1847                    Descriptor[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8);
1848                    Descriptor[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16);
1849                    Descriptor[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24);
1850
1851                    // UncompressedSize - (lower 32 bits) the correct value now
1852                    Descriptor[i++] = (byte)(_UncompressedSize & 0x000000FF);
1853                    Descriptor[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8);
1854                    Descriptor[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16);
1855                    Descriptor[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24);
1856                }
1857
1858                // finally, write the trailing descriptor to the output stream
1859                s.Write(Descriptor, 0, Descriptor.Length);
1860
1861                _LengthOfTrailer += Descriptor.Length;
1862            }
1863        }
1864
1865
1866
1867        private void SetZip64Flags()
1868        {
1869            // zip64 housekeeping
1870            _entryRequiresZip64 = new Nullable<bool>
1871                (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF);
1872
1873            // validate the ZIP64 usage
1874            if (_container.Zip64 == Zip64Option.Never && _entryRequiresZip64.Value)
1875                throw new ZipException("Compressed or Uncompressed size, or offset exceeds the maximum value. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
1876
1877            _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
1878        }
1879
1880
1881
1882        /// <summary>
1883        ///   Prepare the given stream for output - wrap it in a CountingStream, and
1884        ///   then in a CRC stream, and an encryptor and deflator as appropriate.
1885        /// </summary>
1886        /// <remarks>
1887        ///   <para>
1888        ///     Previously this was used in ZipEntry.Write(), but in an effort to
1889        ///     introduce some efficiencies in that method I've refactored to put the
1890        ///     code inline.  This method still gets called by ZipOutputStream.
1891        ///   </para>
1892        /// </remarks>
1893        internal void PrepOutputStream(Stream s,
1894                                       long streamLength,
1895                                       out CountingStream outputCounter,
1896                                       out Stream encryptor,
1897                                       out Stream compressor,
1898                                       out Ionic.Crc.CrcCalculatorStream output)
1899        {
1900            TraceWriteLine("PrepOutputStream: e({0}) comp({1}) crypto({2}) zf({3})",
1901                           FileName,
1902                           CompressionLevel,
1903                           Encryption,
1904                           _container.Name);
1905
1906            // Wrap a counting stream around the raw output stream:
1907            // This is the last thing that happens before the bits go to the
1908            // application-provided stream.
1909            outputCounter = new CountingStream(s);
1910
1911            // Sometimes the incoming "raw" output stream is already a CountingStream.
1912            // Doesn't matter. Wrap it with a counter anyway. We need to count at both
1913            // levels.
1914
1915            if (streamLength != 0L)
1916            {
1917                // Maybe wrap an encrypting stream around that:
1918                // This will happen BEFORE output counting, and AFTER deflation, if encryption
1919                // is used.
1920                encryptor = MaybeApplyEncryption(outputCounter);
1921
1922                // Maybe wrap a compressing Stream around that.
1923                // This will happen BEFORE encryption (if any) as we write data out.
1924                compressor = MaybeApplyCompression(encryptor, streamLength);
1925            }
1926            else
1927            {
1928                encryptor = compressor = outputCounter;
1929            }
1930            // Wrap a CrcCalculatorStream around that.
1931            // This will happen BEFORE compression (if any) as we write data out.
1932            output = new Ionic.Crc.CrcCalculatorStream(compressor, true);
1933        }
1934
1935
1936
1937        private Stream MaybeApplyCompression(Stream s, long streamLength)
1938        {
1939            if (_CompressionMethod == 0x08 && CompressionLevel != Ionic.Zlib.CompressionLevel.None)
1940            {
1941#if !NETCF
1942                // ParallelDeflateThreshold == 0    means ALWAYS use parallel deflate
1943                // ParallelDeflateThreshold == -1L  means NEVER use parallel deflate
1944                // Other values specify the actual threshold.
1945                if (_container.ParallelDeflateThreshold == 0L ||
1946                    (streamLength > _container.ParallelDeflateThreshold &&
1947                     _container.ParallelDeflateThreshold > 0L))
1948                {
1949                    // This is sort of hacky.
1950                    //
1951                    // It's expensive to create a ParallelDeflateOutputStream, because
1952                    // of the large memory buffers.  But the class is unlike most Stream
1953                    // classes in that it can be re-used, so the caller can compress
1954                    // multiple files with it, one file at a time.  The key is to call
1955                    // Reset() on it, in between uses.
1956                    //
1957                    // The ParallelDeflateOutputStream is attached to the container
1958                    // itself - there is just one for the entire ZipFile or
1959                    // ZipOutputStream. So it gets created once, per save, and then
1960                    // re-used many times.
1961                    //
1962                    // This approach will break when we go to a "parallel save"
1963                    // approach, where multiple entries within the zip file are being
1964                    // compressed and saved at the same time.  But for now it's ok.
1965                    //
1966
1967                    // instantiate the ParallelDeflateOutputStream
1968                    if (_container.ParallelDeflater == null)
1969                    {
1970                        _container.ParallelDeflater =
1971                            new ParallelDeflateOutputStream(s,
1972                                                                        CompressionLevel,
1973                                                                       _container.Strategy,
1974                                                                       true);
1975                        // can set the codec buffer size only before the first call to Write().
1976                        if (_container.CodecBufferSize > 0)
1977                            _container.ParallelDeflater.BufferSize = _container.CodecBufferSize;
1978                        if (_container.ParallelDeflateMaxBufferPairs > 0)
1979                            _container.ParallelDeflater.MaxBufferPairs =
1980                                _container.ParallelDeflateMaxBufferPairs;
1981                    }
1982                    // reset it with the new stream
1983                    Ionic.Zlib.ParallelDeflateOutputStream o1 = _container.ParallelDeflater;
1984                    o1.Reset(s);
1985                    return o1;
1986                }
1987#endif
1988                var o = new DeflateStream(s, OfficeOpenXml.Packaging.Ionic.Zlib.CompressionMode.Compress,
1989                                                     CompressionLevel,
1990                                                     true);
1991                if (_container.CodecBufferSize > 0)
1992                    o.BufferSize = _container.CodecBufferSize;
1993                o.Strategy = _container.Strategy;
1994                return o;
1995            }
1996
1997
1998#if BZIP
1999            if (_CompressionMethod == 0x0c)
2000            {
2001#if !NETCF
2002                if (_container.ParallelDeflateThreshold == 0L ||
2003                    (streamLength > _container.ParallelDeflateThreshold &&
2004                     _container.ParallelDeflateThreshold > 0L))
2005                {
2006
2007                    var o1 = new Ionic.BZip2.ParallelBZip2OutputStream(s, true);
2008                    return o1;
2009                }
2010#endif
2011                var o = new Ionic.BZip2.BZip2OutputStream(s, true);
2012                return o;
2013            }
2014#endif
2015
2016            return s;
2017        }
2018
2019
2020
2021        private Stream MaybeApplyEncryption(Stream s)
2022        {
2023            if (Encryption == EncryptionAlgorithm.PkzipWeak)
2024            {
2025                TraceWriteLine("MaybeApplyEncryption: e({0}) PKZIP", FileName);
2026
2027                return new ZipCipherStream(s, _zipCrypto_forWrite, CryptoMode.Encrypt);
2028            }
2029#if AESCRYPTO
2030            if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
2031                     Encryption == EncryptionAlgorithm.WinZipAes256)
2032            {
2033                TraceWriteLine("MaybeApplyEncryption: e({0}) AES", FileName);
2034
2035                return new WinZipAesCipherStream(s, _aesCrypto_forWrite, CryptoMode.Encrypt);
2036            }
2037#endif
2038            TraceWriteLine("MaybeApplyEncryption: e({0}) None", FileName);
2039
2040            return s;
2041        }
2042
2043
2044
2045        private void OnZipErrorWhileSaving(Exception e)
2046        {
2047            if (_container.ZipFile != null)
2048                _ioOperationCanceled = _container.ZipFile.OnZipErrorSaving(this, e);
2049        }
2050
2051
2052
2053        internal void Write(Stream s)
2054        {
2055            var cs1 = s as CountingStream;
2056            var zss1 = s as ZipSegmentedStream;
2057
2058            bool done = false;
2059            do
2060            {
2061                try
2062                {
2063                    // When the app is updating a zip file, it may be possible to
2064                    // just copy data for a ZipEntry from the source zipfile to the
2065                    // destination, as a block, without decompressing and
2066                    // recompressing, etc.  But, in some cases the app modifies the
2067                    // properties on a ZipEntry prior to calling Save(). A change to
2068                    // any of the metadata - the FileName, CompressioLeve and so on,
2069                    // means DotNetZip cannot simply copy through the existing
2070                    // ZipEntry data unchanged.
2071                    //
2072                    // There are two cases:
2073                    //
2074                    //  1. Changes to only metadata, which means the header and
2075                    //     central directory must be changed.
2076                    //
2077                    //  2. Changes to the properties that affect the compressed
2078                    //     stream, such as CompressionMethod, CompressionLevel, or
2079                    //     EncryptionAlgorithm. In this case, DotNetZip must
2080                    //     "re-stream" the data: the old entry data must be maybe
2081                    //     decrypted, maybe decompressed, then maybe re-compressed
2082                    //     and maybe re-encrypted.
2083                    //
2084                    // This test checks if the source for the entry data is a zip file, and
2085                    // if a restream is necessary.  If NOT, then it just copies through
2086                    // one entry, potentially changing the metadata.
2087
2088                    if (_Source == ZipEntrySource.ZipFile && !_restreamRequiredOnSave)
2089                    {
2090                        CopyThroughOneEntry(s);
2091                        return;
2092                    }
2093
2094                    // Is the entry a directory?  If so, the write is relatively simple.
2095                    if (IsDirectory)
2096                    {
2097                        WriteHeader(s, 1);
2098                        StoreRelativeOffset();
2099                        _entryRequiresZip64 = new Nullable<bool>(_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF);
2100                        _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
2101                        // handle case for split archives
2102                        if (zss1 != null)
2103                            _diskNumber = zss1.CurrentSegment;
2104
2105                        return;
2106                    }
2107
2108                    // At this point, the source for this entry is not a directory, and
2109                    // not a previously created zip file, or the source for the entry IS
2110                    // a previously created zip but the settings whave changed in
2111                    // important ways and therefore we will need to process the
2112                    // bytestream (compute crc, maybe compress, maybe encrypt) in order
2113                    // to write the content into the new zip.
2114                    //
2115                    // We do this in potentially 2 passes: The first time we do it as
2116                    // requested, maybe with compression and maybe encryption.  If that
2117                    // causes the bytestream to inflate in size, and if compression was
2118                    // on, then we turn off compression and do it again.
2119
2120
2121                    bool readAgain = true;
2122                    int nCycles = 0;
2123                    do
2124                    {
2125                        nCycles++;
2126
2127                        WriteHeader(s, nCycles);
2128
2129                        // write the encrypted header
2130                        WriteSecurityMetadata(s);
2131
2132                        // write the (potentially compressed, potentially encrypted) file data
2133                        _WriteEntryData(s);
2134
2135                        // track total entry size (including the trailing descriptor and MAC)
2136                        _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer;
2137
2138                        // The file data has now been written to the stream, and
2139                        // the file pointer is positioned directly after file data.
2140
2141                        if (nCycles > 1) readAgain = false;
2142                        else if (!s.CanSeek) readAgain = false;
2143                        else readAgain = WantReadAgain();
2144
2145                        if (readAgain)
2146                        {
2147                            // Seek back in the raw output stream, to the beginning of the file
2148                            // data for this entry.
2149
2150                            // handle case for split archives
2151                            if (zss1 != null)
2152                            {
2153                                // Console.WriteLine("***_diskNumber/first: {0}", _diskNumber);
2154                                // Console.WriteLine("***_diskNumber/current: {0}", zss.CurrentSegment);
2155                                zss1.TruncateBackward(_diskNumber, _RelativeOffsetOfLocalHeader);
2156                            }
2157                            else
2158                                // workitem 8098: ok (output).
2159                                s.Seek(_RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
2160
2161                            // If the last entry expands, we read again; but here, we must
2162                            // truncate the stream to prevent garbage data after the
2163                            // end-of-central-directory.
2164
2165                            // workitem 8098: ok (output).
2166                            s.SetLength(s.Position);
2167
2168                            // Adjust the count on the CountingStream as necessary.
2169                            if (cs1 != null) cs1.Adjust(_TotalEntrySize);
2170                        }
2171                    }
2172                    while (readAgain);
2173                    _skippedDuringSave = false;
2174                    done = true;
2175                }
2176                catch (System.Exception exc1)
2177                {
2178                    ZipErrorAction orig = this.ZipErrorAction;
2179                    int loop = 0;
2180                    do
2181                    {
2182                        if (ZipErrorAction == ZipErrorAction.Throw)
2183                            throw;
2184
2185                        if (ZipErrorAction == ZipErrorAction.Skip ||
2186                            ZipErrorAction == ZipErrorAction.Retry)
2187                        {
2188                            // must reset file pointer here.
2189                            // workitem 13903 - seek back only when necessary
2190                            long p1 = (cs1 != null)
2191                                ? cs1.ComputedPosition
2192                                : s.Position;
2193                            long delta = p1 - _future_ROLH;
2194                            if (delta > 0)
2195                            {
2196                                s.Seek(delta, SeekOrigin.Current); // may throw
2197                                long p2 = s.Position;
2198                                s.SetLength(s.Position);  // to prevent garbage if this is the last entry
2199                                if (cs1 != null) cs1.Adjust(p1 - p2);
2200                            }
2201                            if (ZipErrorAction == ZipErrorAction.Skip)
2202                            {
2203                                WriteStatus("Skipping file {0} (exception: {1})", LocalFileName, exc1.ToString());
2204
2205                                _skippedDuringSave = true;
2206                                done = true;
2207                            }
2208                            else
2209                                this.ZipErrorAction = orig;
2210                            break;
2211                        }
2212
2213                        if (loop > 0) throw;
2214
2215                        if (ZipErrorAction == ZipErrorAction.InvokeErrorEvent)
2216                        {
2217                            OnZipErrorWhileSaving(exc1);
2218                            if (_ioOperationCanceled)
2219                            {
2220                                done = true;
2221                                break;
2222                            }
2223                        }
2224                        loop++;
2225                    }
2226                    while (true);
2227                }
2228            }
2229            while (!done);
2230        }
2231
2232
2233        internal void StoreRelativeOffset()
2234        {
2235            _RelativeOffsetOfLocalHeader = _future_ROLH;
2236        }
2237
2238
2239
2240        internal void NotifySaveComplete()
2241        {
2242            // When updating a zip file, there are two contexts for properties
2243            // like Encryption or CompressionMethod - the values read from the
2244            // original zip file, and the values used in the updated zip file.
2245            // The _FromZipFile versions are the originals.  At the end of a save,
2246            // these values are the same.  So we need to update them.  This takes
2247            // care of the boundary case where a single zipfile instance can be
2248            // saved multiple times, with distinct changes to the properties on
2249            // the entries, in between each Save().
2250            _Encryption_FromZipFile = _Encryption;
2251            _CompressionMethod_FromZipFile = _CompressionMethod;
2252            _restreamRequiredOnSave = false;
2253            _metadataChanged = false;
2254            //_Source = ZipEntrySource.None;
2255            _Source = ZipEntrySource.ZipFile; // workitem 10694
2256        }
2257
2258
2259        internal void WriteSecurityMetadata(Stream outstream)
2260        {
2261            if (Encryption == EncryptionAlgorithm.None)
2262                return;
2263
2264            string pwd = this._Password;
2265
2266            // special handling for source == ZipFile.
2267            // Want to support the case where we re-stream an encrypted entry. This will involve,
2268            // at runtime, reading, decrypting, and decompressing from the original zip file, then
2269            // compressing, encrypting, and writing to the output zip file.
2270
2271            // If that's what we're doing, and the password hasn't been set on the entry,
2272            // we use the container (ZipFile/ZipOutputStream) password to decrypt.
2273            // This test here says to use the container password to re-encrypt, as well,
2274            // with that password, if the entry password is null.
2275
2276            if (this._Source == ZipEntrySource.ZipFile && pwd == null)
2277                pwd = this._container.Password;
2278
2279            if (pwd == null)
2280            {
2281                _zipCrypto_forWrite = null;
2282#if AESCRYPTO
2283                _aesCrypto_forWrite = null;
2284#endif
2285                return;
2286            }
2287
2288            TraceWriteLine("WriteSecurityMetadata: e({0}) crypto({1}) pw({2})",
2289                           FileName, Encryption.ToString(), pwd);
2290
2291            if (Encryption == EncryptionAlgorithm.PkzipWeak)
2292            {
2293                // If PKZip (weak) encryption is in use, then the encrypted entry data
2294                // is preceded by 12-byte "encryption header" for the entry.
2295
2296                _zipCrypto_forWrite = ZipCrypto.ForWrite(pwd);
2297
2298                // generate the random 12-byte header:
2299                var rnd = new System.Random();
2300                byte[] encryptionHeader = new byte[12];
2301                rnd.NextBytes(encryptionHeader);
2302
2303                // workitem 8271
2304                if ((this._BitField & 0x0008) == 0x0008)
2305                {
2306                    // In the case that bit 3 of the general purpose bit flag is set to
2307                    // indicate the presence of a 'data descriptor' (signature
2308                    // 0x08074b50), the last byte of the decrypted header is sometimes
2309                    // compared with the high-order byte of the lastmodified time,
2310                    // rather than the high-order byte of the CRC, to verify the
2311                    // password.
2312                    //
2313                    // This is not documented in the PKWare Appnote.txt.
2314                    // This was discovered this by analysis of the Crypt.c source file in the
2315                    // InfoZip library
2316                    // http://www.info-zip.org/pub/infozip/
2317
2318                    // Also, winzip insists on this!
2319                    _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified);
2320                    encryptionHeader[11] = (byte)((this._TimeBlob >> 8) & 0xff);
2321                }
2322                else
2323                {
2324                    // When bit 3 is not set, the CRC value is required before
2325                    // encryption of the file data begins. In this case there is no way
2326                    // around it: must read the stream in its entirety to compute the
2327                    // actual CRC before proceeding.
2328                    FigureCrc32();
2329                    encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff);
2330                }
2331
2332                // Encrypt the random header, INCLUDING the final byte which is either
2333                // the high-order byte of the CRC32, or the high-order byte of the
2334                // _TimeBlob.  Must do this BEFORE encrypting the file data.  This
2335                // step changes the state of the cipher, or in the words of the PKZIP
2336                // spec, it "further initializes" the cipher keys.
2337
2338                byte[] cipherText = _zipCrypto_forWrite.EncryptMessage(encryptionHeader, encryptionHeader.Length);
2339
2340                // Write the ciphered bonafide encryption header.
2341                outstream.Write(cipherText, 0, cipherText.Length);
2342                _LengthOfHeader += cipherText.Length;  // 12 bytes
2343            }
2344
2345#if AESCRYPTO
2346            else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
2347                Encryption == EncryptionAlgorithm.WinZipAes256)
2348            {
2349                // If WinZip AES encryption is in use, then the encrypted entry data is
2350                // preceded by a variable-sized Salt and a 2-byte "password
2351                // verification" value for the entry.
2352
2353                int keystrength = GetKeyStrengthInBits(Encryption);
2354                _aesCrypto_forWrite = WinZipAesCrypto.Generate(pwd, keystrength);
2355                outstream.Write(_aesCrypto_forWrite.Salt, 0, _aesCrypto_forWrite._Salt.Length);
2356                outstream.Write(_aesCrypto_forWrite.GeneratedPV, 0, _aesCrypto_forWrite.GeneratedPV.Length);
2357                _LengthOfHeader += _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length;
2358
2359                TraceWriteLine("WriteSecurityMetadata: AES e({0}) keybits({1}) _LOH({2})",
2360                               FileName, keystrength, _LengthOfHeader);
2361
2362            }
2363#endif
2364
2365        }
2366
2367
2368
2369        private void CopyThroughOneEntry(Stream outStream)
2370        {
2371            // Just read the entry from the existing input zipfile and write to the output.
2372            // But, if metadata has changed (like file times or attributes), or if the ZIP64
2373            // option has changed, we can re-stream the entry data but must recompute the
2374            // metadata.
2375            if (this.LengthOfHeader == 0)
2376                throw new BadStateException("Bad header length.");
2377
2378            // is it necessary to re-constitute new metadata for this entry?
2379            bool needRecompute = _metadataChanged ||
2380                (this.ArchiveStream is ZipSegmentedStream) ||
2381                (outStream is ZipSegmentedStream) ||
2382                (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) ||
2383                (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always);
2384
2385            if (needRecompute)
2386                CopyThroughWithRecompute(outStream);
2387            else
2388                CopyThroughWithNoChange(outStream);
2389
2390            // zip64 housekeeping
2391            _entryRequiresZip64 = new Nullable<bool>
2392                (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF ||
2393                _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF
2394                );
2395
2396            _OutputUsesZip64 = new Nullable<bool>(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value);
2397        }
2398
2399
2400
2401        private void CopyThroughWithRecompute(Stream outstream)
2402        {
2403            int n;
2404            byte[] bytes = new byte[BufferSize];
2405            var input = new CountingStream(this.ArchiveStream);
2406
2407            long origRelativeOffsetOfHeader = _RelativeOffsetOfLocalHeader;
2408
2409            // The header length may change due to rename of file, add a comment, etc.
2410            // We need to retain the original.
2411            int origLengthOfHeader = LengthOfHeader; // including crypto bytes!
2412
2413            // WriteHeader() has the side effect of changing _RelativeOffsetOfLocalHeader
2414            // and setting _LengthOfHeader.  While ReadHeader() reads the crypto header if
2415            // present, WriteHeader() does not write the crypto header.
2416            WriteHeader(outstream, 0);
2417            StoreRelativeOffset();
2418
2419            if (!this.FileName.EndsWith("/"))
2420            {
2421                // Not a directory; there is file data.
2422                // Seek to the beginning of the entry data in the input stream.
2423
2424                long pos = origRelativeOffsetOfHeader + origLengthOfHeader;
2425                int len = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile);
2426                pos -= len; // want to keep the crypto header
2427                _LengthOfHeader += len;
2428
2429                input.Seek(pos, SeekOrigin.Begin);
2430
2431                // copy through everything after the header to the output stream
2432                long remaining = this._CompressedSize;
2433
2434                while (remaining > 0)
2435                {
2436                    len = (remaining > bytes.Length) ? bytes.Length : (int)remaining;
2437
2438                    // read
2439                    n = input.Read(bytes, 0, len);
2440                    //_CheckRead(n);
2441
2442                    // write
2443                    outstream.Write(bytes, 0, n);
2444                    remaining -= n;
2445                    OnWriteBlock(input.BytesRead, this._CompressedSize);
2446                    if (_ioOperationCanceled)
2447                        break;
2448                }
2449
2450                // bit 3 descriptor
2451                if ((this._BitField & 0x0008) == 0x0008)
2452                {
2453                    int size = 16;
2454                    if (_InputUsesZip64) size += 8;
2455                    byte[] Descriptor = new byte[size];
2456                    input.Read(Descriptor, 0, size);
2457
2458                    if (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never)
2459                    {
2460                        // original descriptor was 24 bytes, now we need 16.
2461                        // Must check for underflow here.
2462                        // signature + CRC.
2463                        outstream.Write(Descriptor, 0, 8);
2464
2465                        // Compressed
2466                        if (_CompressedSize > 0xFFFFFFFF)
2467                            throw new InvalidOperationException("ZIP64 is required");
2468                        outstream.Write(Descriptor, 8, 4);
2469
2470                        // UnCompressed
2471                        if (_UncompressedSize > 0xFFFFFFFF)
2472                            throw new InvalidOperationException("ZIP64 is required");
2473                        outstream.Write(Descriptor, 16, 4);
2474                        _LengthOfTrailer -= 8;
2475                    }
2476                    else if (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always)
2477                    {
2478                        // original descriptor was 16 bytes, now we need 24
2479                        // signature + CRC
2480                        byte[] pad = new byte[4];
2481                        outstream.Write(Descriptor, 0, 8);
2482                        // Compressed
2483                        outstream.Write(Descriptor, 8, 4);
2484                        outstream.Write(pad, 0, 4);
2485                        // UnCompressed
2486                        outstream.Write(Descriptor, 12, 4);
2487                        outstream.Write(pad, 0, 4);
2488                        _LengthOfTrailer += 8;
2489                    }
2490                    else
2491                    {
2492                        // same descriptor on input and output. Copy it through.
2493                        outstream.Write(Descriptor, 0, size);
2494                        //_LengthOfTrailer += size;
2495                    }
2496                }
2497            }
2498
2499            _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer;
2500        }
2501
2502
2503        private void CopyThroughWithNoChange(Stream outstream)
2504        {
2505            int n;
2506            byte[] bytes = new byte[BufferSize];
2507            var input = new CountingStream(this.ArchiveStream);
2508
2509            // seek to the beginning of the entry data in the input stream
2510            input.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
2511
2512            if (this._TotalEntrySize == 0)
2513            {
2514                // We've never set the length of the entry.
2515                // Set it here.
2516                this._TotalEntrySize = this._LengthOfHeader + this._CompressedFileDataSize + _LengthOfTrailer;
2517
2518                // The CompressedSize includes all the leading metadata associated
2519                // to encryption, if any, as well as the compressed data, or
2520                // compressed-then-encrypted data, and the trailer in case of AES.
2521
2522                // The CompressedFileData size is the same, less the encryption
2523                // framing data (12 bytes header for PKZip; 10/18 bytes header and
2524                // 10 byte trailer for AES).
2525
2526                // The _LengthOfHeader includes all the zip entry header plus the
2527                // crypto header, if any.  The _LengthOfTrailer includes the
2528                // 10-byte MAC for AES, where appropriate, and the bit-3
2529                // Descriptor, where applicable.
2530            }
2531
2532
2533            // workitem 5616
2534            // remember the offset, within the output stream, of this particular entry header.
2535            // This may have changed if any of the other entries changed (eg, if a different
2536            // entry was removed or added.)
2537            var counter = outstream as CountingStream;
2538            _RelativeOffsetOfLocalHeader = (counter != null)
2539                ? counter.ComputedPosition
2540                : outstream.Position;  // BytesWritten
2541
2542            // copy through the header, filedata, trailer, everything...
2543            long remaining = this._TotalEntrySize;
2544            while (remaining > 0)
2545            {
2546                int len = (remaining > bytes.Length) ? bytes.Length : (int)remaining;
2547
2548                // read
2549                n = input.Read(bytes, 0, len);
2550                //_CheckRead(n);
2551
2552                // write
2553                outstream.Write(bytes, 0, n);
2554                remaining -= n;
2555                OnWriteBlock(input.BytesRead, this._TotalEntrySize);
2556                if (_ioOperationCanceled)
2557                    break;
2558            }
2559        }
2560
2561
2562
2563
2564        [System.Diagnostics.ConditionalAttribute("Trace")]
2565        private void TraceWriteLine(string format, params object[] varParams)
2566        {
2567            lock (_outputLock)
2568            {
2569                int tid = System.Threading.Thread.CurrentThread.GetHashCode();
2570#if ! (NETCF || SILVERLIGHT)
2571                Console.ForegroundColor = (ConsoleColor)(tid % 8 + 8);
2572#endif
2573                Console.Write("{0:000} ZipEntry.Write ", tid);
2574                Console.WriteLine(format, varParams);
2575#if ! (NETCF || SILVERLIGHT)
2576                Console.ResetColor();
2577#endif
2578            }
2579        }
2580
2581        private object _outputLock = new Object();
2582    }
2583}
Note: See TracBrowser for help on using the repository browser.