Merge pull request #1003 from robertpi/Bug12211
[mono.git] / mcs / class / System.IO.Compression / Ionic / Zip / ZipOutputStream.cs
1 // ZipOutputStream.cs
2 //
3 // ------------------------------------------------------------------
4 //
5 // Copyright (c) 2009 Dino Chiesa.
6 // All rights reserved.
7 //
8 // This code module is part of DotNetZip, a zipfile class library.
9 //
10 // ------------------------------------------------------------------
11 //
12 // This code is licensed under the Microsoft Public License.
13 // See the file License.txt for the license details.
14 // More info on: http://dotnetzip.codeplex.com
15 //
16 // ------------------------------------------------------------------
17 //
18 // last saved (in emacs):
19 // Time-stamp: <2011-July-28 06:34:30>
20 //
21 // ------------------------------------------------------------------
22 //
23 // This module defines the ZipOutputStream class, which is a stream metaphor for
24 // generating zip files.  This class does not depend on Ionic.Zip.ZipFile, but rather
25 // stands alongside it as an alternative "container" for ZipEntry.  It replicates a
26 // subset of the properties, including these:
27 //
28 //  - Comment
29 //  - Encryption
30 //  - Password
31 //  - CodecBufferSize
32 //  - CompressionLevel
33 //  - CompressionMethod
34 //  - EnableZip64 (UseZip64WhenSaving)
35 //  - IgnoreCase (!CaseSensitiveRetrieval)
36 //
37 // It adds these novel methods:
38 //
39 //  - PutNextEntry
40 //
41 //
42 // ------------------------------------------------------------------
43 //
44
45 using System;
46 using System.Threading;
47 using System.Collections.Generic;
48 using System.IO;
49 using Ionic.Zip;
50
51 namespace Ionic.Zip
52 {
53     /// <summary>
54     ///   Provides a stream metaphor for generating zip files.
55     /// </summary>
56     ///
57     /// <remarks>
58     /// <para>
59     ///   This class writes zip files, as defined in the <see
60     ///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">specification
61     ///   for zip files described by PKWare</see>.  The compression for this
62     ///   implementation is provided by a managed-code version of Zlib, included with
63     ///   DotNetZip in the classes in the Ionic.Zlib namespace.
64     /// </para>
65     ///
66     /// <para>
67     ///   This class provides an alternative programming model to the one enabled by the
68     ///   <see cref="ZipFile"/> class. Use this when creating zip files, as an
69     ///   alternative to the <see cref="ZipFile"/> class, when you would like to use a
70     ///   <c>Stream</c> type to write the zip file.
71     /// </para>
72     ///
73     /// <para>
74     ///   Both the <c>ZipOutputStream</c> class and the <c>ZipFile</c> class can be used
75     ///   to create zip files. Both of them support many of the common zip features,
76     ///   including Unicode, different compression levels, and ZIP64.   They provide
77     ///   very similar performance when creating zip files.
78     /// </para>
79     ///
80     /// <para>
81     ///   The <c>ZipFile</c> class is generally easier to use than
82     ///   <c>ZipOutputStream</c> and should be considered a higher-level interface.  For
83     ///   example, when creating a zip file via calls to the <c>PutNextEntry()</c> and
84     ///   <c>Write()</c> methods on the <c>ZipOutputStream</c> class, the caller is
85     ///   responsible for opening the file, reading the bytes from the file, writing
86     ///   those bytes into the <c>ZipOutputStream</c>, setting the attributes on the
87     ///   <c>ZipEntry</c>, and setting the created, last modified, and last accessed
88     ///   timestamps on the zip entry. All of these things are done automatically by a
89     ///   call to <see cref="ZipFile.AddFile(string,string)">ZipFile.AddFile()</see>.
90     ///   For this reason, the <c>ZipOutputStream</c> is generally recommended for use
91     ///   only when your application emits arbitrary data, not necessarily data from a
92     ///   filesystem file, directly into a zip file, and does so using a <c>Stream</c>
93     ///   metaphor.
94     /// </para>
95     ///
96     /// <para>
97     ///   Aside from the differences in programming model, there are other
98     ///   differences in capability between the two classes.
99     /// </para>
100     ///
101     /// <list type="bullet">
102     ///   <item>
103     ///     <c>ZipFile</c> can be used to read and extract zip files, in addition to
104     ///     creating zip files. <c>ZipOutputStream</c> cannot read zip files. If you want
105     ///     to use a stream to read zip files, check out the <see cref="ZipInputStream"/> class.
106     ///   </item>
107     ///
108     ///   <item>
109     ///     <c>ZipOutputStream</c> does not support the creation of segmented or spanned
110     ///     zip files.
111     ///   </item>
112     ///
113     ///   <item>
114     ///     <c>ZipOutputStream</c> cannot produce a self-extracting archive.
115     ///   </item>
116     /// </list>
117     ///
118     /// <para>
119     ///   Be aware that the <c>ZipOutputStream</c> class implements the <see
120     ///   cref="System.IDisposable"/> interface.  In order for
121     ///   <c>ZipOutputStream</c> to produce a valid zip file, you use use it within
122     ///   a using clause (<c>Using</c> in VB), or call the <c>Dispose()</c> method
123     ///   explicitly.  See the examples for how to employ a using clause.
124     /// </para>
125     ///
126     /// <para>
127     ///   Also, a note regarding compression performance: On the desktop .NET
128     ///   Framework, DotNetZip can use a multi-threaded compression implementation
129     ///   that provides significant speed increases on large files, over 300k or so,
130     ///   at the cost of increased memory use at runtime.  (The output of the
131     ///   compression is almost exactly the same size).  But, the multi-threaded
132     ///   approach incurs a performance hit on smaller files. There's no way for the
133     ///   ZipOutputStream to know whether parallel compression will be beneficial,
134     ///   because the ZipOutputStream does not know how much data you will write
135     ///   through the stream.  You may wish to set the <see
136     ///   cref="ParallelDeflateThreshold"/> property to zero, if you are compressing
137     ///   large files through <c>ZipOutputStream</c>.  This will cause parallel
138     ///   compression to be used, always.
139     /// </para>
140     /// </remarks>
141     internal class ZipOutputStream : Stream
142     {
143         /// <summary>
144         ///   Create a ZipOutputStream, wrapping an existing stream.
145         /// </summary>
146         ///
147         /// <remarks>
148         /// <para>
149         ///   The <see cref="ZipFile"/> class is generally easier to use when creating
150         ///   zip files. The ZipOutputStream offers a different metaphor for creating a
151         ///   zip file, based on the <see cref="System.IO.Stream"/> class.
152         /// </para>
153         ///
154         /// </remarks>
155         ///
156         /// <param name="stream">
157         /// The stream to wrap. It must be writable. This stream will be closed at
158         /// the time the ZipOutputStream is closed.
159         /// </param>
160         ///
161         /// <example>
162         ///
163         ///   This example shows how to create a zip file, using the
164         ///   ZipOutputStream class.
165         ///
166         /// <code lang="C#">
167         /// private void Zipup()
168         /// {
169         ///     if (filesToZip.Count == 0)
170         ///     {
171         ///         System.Console.WriteLine("Nothing to do.");
172         ///         return;
173         ///     }
174         ///
175         ///     using (var raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
176         ///     {
177         ///         using (var output= new ZipOutputStream(raw))
178         ///         {
179         ///             output.Password = "VerySecret!";
180         ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
181         ///
182         ///             foreach (string inputFileName in filesToZip)
183         ///             {
184         ///                 System.Console.WriteLine("file: {0}", inputFileName);
185         ///
186         ///                 output.PutNextEntry(inputFileName);
187         ///                 using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write ))
188         ///                 {
189         ///                     byte[] buffer= new byte[2048];
190         ///                     int n;
191         ///                     while ((n= input.Read(buffer,0,buffer.Length)) > 0)
192         ///                     {
193         ///                         output.Write(buffer,0,n);
194         ///                     }
195         ///                 }
196         ///             }
197         ///         }
198         ///     }
199         /// }
200         /// </code>
201         ///
202         /// <code lang="VB">
203         /// Private Sub Zipup()
204         ///     Dim outputFileName As String = "XmlData.zip"
205         ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
206         ///     If (filesToZip.Length = 0) Then
207         ///         Console.WriteLine("Nothing to do.")
208         ///     Else
209         ///         Using raw As FileStream = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite)
210         ///             Using output As ZipOutputStream = New ZipOutputStream(raw)
211         ///                 output.Password = "VerySecret!"
212         ///                 output.Encryption = EncryptionAlgorithm.WinZipAes256
213         ///                 Dim inputFileName As String
214         ///                 For Each inputFileName In filesToZip
215         ///                     Console.WriteLine("file: {0}", inputFileName)
216         ///                     output.PutNextEntry(inputFileName)
217         ///                     Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
218         ///                         Dim n As Integer
219         ///                         Dim buffer As Byte() = New Byte(2048) {}
220         ///                         Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
221         ///                             output.Write(buffer, 0, n)
222         ///                         Loop
223         ///                     End Using
224         ///                 Next
225         ///             End Using
226         ///         End Using
227         ///     End If
228         /// End Sub
229         /// </code>
230         /// </example>
231         public ZipOutputStream(Stream stream) : this(stream, false) { }
232
233
234         /// <summary>
235         ///   Create a ZipOutputStream that writes to a filesystem file.
236         /// </summary>
237         ///
238         /// <remarks>
239         ///   The <see cref="ZipFile"/> class is generally easier to use when creating
240         ///   zip files. The ZipOutputStream offers a different metaphor for creating a
241         ///   zip file, based on the <see cref="System.IO.Stream"/> class.
242         /// </remarks>
243         ///
244         /// <param name="fileName">
245         ///   The name of the zip file to create.
246         /// </param>
247         ///
248         /// <example>
249         ///
250         ///   This example shows how to create a zip file, using the
251         ///   ZipOutputStream class.
252         ///
253         /// <code lang="C#">
254         /// private void Zipup()
255         /// {
256         ///     if (filesToZip.Count == 0)
257         ///     {
258         ///         System.Console.WriteLine("Nothing to do.");
259         ///         return;
260         ///     }
261         ///
262         ///     using (var output= new ZipOutputStream(outputFileName))
263         ///     {
264         ///         output.Password = "VerySecret!";
265         ///         output.Encryption = EncryptionAlgorithm.WinZipAes256;
266         ///
267         ///         foreach (string inputFileName in filesToZip)
268         ///         {
269         ///             System.Console.WriteLine("file: {0}", inputFileName);
270         ///
271         ///             output.PutNextEntry(inputFileName);
272         ///             using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read,
273         ///                                          FileShare.Read | FileShare.Write ))
274         ///             {
275         ///                 byte[] buffer= new byte[2048];
276         ///                 int n;
277         ///                 while ((n= input.Read(buffer,0,buffer.Length)) > 0)
278         ///                 {
279         ///                     output.Write(buffer,0,n);
280         ///                 }
281         ///             }
282         ///         }
283         ///     }
284         /// }
285         /// </code>
286         ///
287         /// <code lang="VB">
288         /// Private Sub Zipup()
289         ///     Dim outputFileName As String = "XmlData.zip"
290         ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
291         ///     If (filesToZip.Length = 0) Then
292         ///         Console.WriteLine("Nothing to do.")
293         ///     Else
294         ///         Using output As ZipOutputStream = New ZipOutputStream(outputFileName)
295         ///             output.Password = "VerySecret!"
296         ///             output.Encryption = EncryptionAlgorithm.WinZipAes256
297         ///             Dim inputFileName As String
298         ///             For Each inputFileName In filesToZip
299         ///                 Console.WriteLine("file: {0}", inputFileName)
300         ///                 output.PutNextEntry(inputFileName)
301         ///                 Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
302         ///                     Dim n As Integer
303         ///                     Dim buffer As Byte() = New Byte(2048) {}
304         ///                     Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
305         ///                         output.Write(buffer, 0, n)
306         ///                     Loop
307         ///                 End Using
308         ///             Next
309         ///         End Using
310         ///     End If
311         /// End Sub
312         /// </code>
313         /// </example>
314         public ZipOutputStream(String fileName)
315         {
316             Stream stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
317             _Init(stream, false, fileName);
318         }
319
320
321         /// <summary>
322         ///   Create a ZipOutputStream.
323         /// </summary>
324         ///
325         /// <remarks>
326         ///   See the documentation for the <see
327         ///   cref="ZipOutputStream(Stream)">ZipOutputStream(Stream)</see>
328         ///   constructor for an example.
329         /// </remarks>
330         ///
331         /// <param name="stream">
332         ///   The stream to wrap. It must be writable.
333         /// </param>
334         ///
335         /// <param name="leaveOpen">
336         ///   true if the application would like the stream
337         ///   to remain open after the <c>ZipOutputStream</c> has been closed.
338         /// </param>
339         public ZipOutputStream(Stream stream, bool leaveOpen)
340         {
341             _Init(stream, leaveOpen, null);
342         }
343
344         private void _Init(Stream stream, bool leaveOpen, string name)
345         {
346             // workitem 9307
347             _outputStream = stream.CanRead ? stream : new CountingStream(stream);
348             CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
349             CompressionMethod = Ionic.Zip.CompressionMethod.Deflate;
350             _encryption = EncryptionAlgorithm.None;
351             _entriesWritten = new Dictionary<String, ZipEntry>(StringComparer.Ordinal);
352             _zip64 = Zip64Option.Never;
353             _leaveUnderlyingStreamOpen = leaveOpen;
354             Strategy = Ionic.Zlib.CompressionStrategy.Default;
355             _name = name ?? "(stream)";
356 #if !NETCF
357             ParallelDeflateThreshold = -1L;
358 #endif
359         }
360
361
362         /// <summary>Provides a string representation of the instance.</summary>
363         /// <remarks>
364         ///   <para>
365         ///     This can be useful for debugging purposes.
366         ///   </para>
367         /// </remarks>
368         /// <returns>a string representation of the instance.</returns>
369         public override String ToString()
370         {
371             return String.Format ("ZipOutputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen);
372         }
373
374
375         /// <summary>
376         ///   Sets the password to be used on the <c>ZipOutputStream</c> instance.
377         /// </summary>
378         ///
379         /// <remarks>
380         ///
381         /// <para>
382         ///   When writing a zip archive, this password is applied to the entries, not
383         ///   to the zip archive itself. It applies to any <c>ZipEntry</c> subsequently
384         ///   written to the <c>ZipOutputStream</c>.
385         /// </para>
386         ///
387         /// <para>
388         ///   Using a password does not encrypt or protect the "directory" of the
389         ///   archive - the list of entries contained in the archive.  If you set the
390         ///   <c>Password</c> property, the password actually applies to individual
391         ///   entries that are added to the archive, subsequent to the setting of this
392         ///   property.  The list of filenames in the archive that is eventually created
393         ///   will appear in clear text, but the contents of the individual files are
394         ///   encrypted.  This is how Zip encryption works.
395         /// </para>
396         ///
397         /// <para>
398         ///   If you set this property, and then add a set of entries to the archive via
399         ///   calls to <c>PutNextEntry</c>, then each entry is encrypted with that
400         ///   password.  You may also want to change the password between adding
401         ///   different entries. If you set the password, add an entry, then set the
402         ///   password to <c>null</c> (<c>Nothing</c> in VB), and add another entry, the
403         ///   first entry is encrypted and the second is not.
404         /// </para>
405         ///
406         /// <para>
407         ///   When setting the <c>Password</c>, you may also want to explicitly set the <see
408         ///   cref="Encryption"/> property, to specify how to encrypt the entries added
409         ///   to the ZipFile.  If you set the <c>Password</c> to a non-null value and do not
410         ///   set <see cref="Encryption"/>, then PKZip 2.0 ("Weak") encryption is used.
411         ///   This encryption is relatively weak but is very interoperable. If
412         ///   you set the password to a <c>null</c> value (<c>Nothing</c> in VB),
413         ///   <c>Encryption</c> is reset to None.
414         /// </para>
415         ///
416         /// <para>
417         ///   Special case: if you wrap a ZipOutputStream around a non-seekable stream,
418         ///   and use encryption, and emit an entry of zero bytes, the <c>Close()</c> or
419         ///   <c>PutNextEntry()</c> following the entry will throw an exception.
420         /// </para>
421         ///
422         /// </remarks>
423         public String Password
424         {
425             set
426             {
427                 if (_disposed)
428                 {
429                     _exceptionPending = true;
430                     throw new System.InvalidOperationException("The stream has been closed.");
431                 }
432
433                 _password = value;
434                 if (_password == null)
435                 {
436                     _encryption = EncryptionAlgorithm.None;
437                 }
438                 else if (_encryption == EncryptionAlgorithm.None)
439                 {
440                     _encryption = EncryptionAlgorithm.PkzipWeak;
441                 }
442             }
443         }
444
445
446         /// <summary>
447         ///   The Encryption to use for entries added to the <c>ZipOutputStream</c>.
448         /// </summary>
449         ///
450         /// <remarks>
451         /// <para>
452         ///   The specified Encryption is applied to the entries subsequently
453         ///   written to the <c>ZipOutputStream</c> instance.
454         /// </para>
455         ///
456         /// <para>
457         ///   If you set this to something other than
458         ///   EncryptionAlgorithm.None, you will also need to set the
459         ///   <see cref="Password"/> to a non-null, non-empty value in
460         ///   order to actually get encryption on the entry.
461         /// </para>
462         ///
463         /// </remarks>
464         ///
465         /// <seealso cref="Password">ZipOutputStream.Password</seealso>
466         /// <seealso cref="Ionic.Zip.ZipEntry.Encryption">ZipEntry.Encryption</seealso>
467         public EncryptionAlgorithm Encryption
468         {
469             get
470             {
471                 return _encryption;
472             }
473             set
474             {
475                 if (_disposed)
476                 {
477                     _exceptionPending = true;
478                     throw new System.InvalidOperationException("The stream has been closed.");
479                 }
480                 if (value == EncryptionAlgorithm.Unsupported)
481                 {
482                     _exceptionPending = true;
483                     throw new InvalidOperationException("You may not set Encryption to that value.");
484                 }
485                 _encryption = value;
486             }
487         }
488
489
490         /// <summary>
491         ///   Size of the work buffer to use for the ZLIB codec during compression.
492         /// </summary>
493         ///
494         /// <remarks>
495         ///   Setting this may affect performance.  For larger files, setting this to a
496         ///   larger size may improve performance, but I'm not sure.  Sorry, I don't
497         ///   currently have good recommendations on how to set it.  You can test it if
498         ///   you like.
499         /// </remarks>
500         public int CodecBufferSize
501         {
502             get;
503             set;
504         }
505
506
507         /// <summary>
508         ///   The compression strategy to use for all entries.
509         /// </summary>
510         ///
511         /// <remarks>
512         ///   Set the Strategy used by the ZLIB-compatible compressor, when compressing
513         ///   data for the entries in the zip archive. Different compression strategies
514         ///   work better on different sorts of data. The strategy parameter can affect
515         ///   the compression ratio and the speed of compression but not the correctness
516         ///   of the compresssion.  For more information see <see
517         ///   cref="Ionic.Zlib.CompressionStrategy "/>.
518         /// </remarks>
519         public Ionic.Zlib.CompressionStrategy Strategy
520         {
521             get;
522             set;
523         }
524
525
526         /// <summary>
527         ///   The type of timestamp attached to the ZipEntry.
528         /// </summary>
529         ///
530         /// <remarks>
531         ///   Set this in order to specify the kind of timestamp that should be emitted
532         ///   into the zip file for each entry.
533         /// </remarks>
534         public ZipEntryTimestamp Timestamp
535         {
536             get
537             {
538                 return _timestamp;
539             }
540             set
541             {
542                 if (_disposed)
543                 {
544                     _exceptionPending = true;
545                     throw new System.InvalidOperationException("The stream has been closed.");
546                 }
547                 _timestamp = value;
548             }
549         }
550
551
552         /// <summary>
553         ///   Sets the compression level to be used for entries subsequently added to
554         ///   the zip archive.
555         /// </summary>
556         ///
557         /// <remarks>
558         ///  <para>
559         ///    Varying the compression level used on entries can affect the
560         ///    size-vs-speed tradeoff when compression and decompressing data streams
561         ///    or files.
562         ///  </para>
563         ///
564         ///  <para>
565         ///    As with some other properties on the <c>ZipOutputStream</c> class, like <see
566         ///    cref="Password"/>, and <see cref="Encryption"/>,
567         ///    setting this property on a <c>ZipOutputStream</c>
568         ///    instance will cause the specified <c>CompressionLevel</c> to be used on all
569         ///    <see cref="ZipEntry"/> items that are subsequently added to the
570         ///    <c>ZipOutputStream</c> instance.
571         ///  </para>
572         ///
573         ///  <para>
574         ///    If you do not set this property, the default compression level is used,
575         ///    which normally gives a good balance of compression efficiency and
576         ///    compression speed.  In some tests, using <c>BestCompression</c> can
577         ///    double the time it takes to compress, while delivering just a small
578         ///    increase in compression efficiency.  This behavior will vary with the
579         ///    type of data you compress.  If you are in doubt, just leave this setting
580         ///    alone, and accept the default.
581         ///  </para>
582         /// </remarks>
583         public Ionic.Zlib.CompressionLevel CompressionLevel
584         {
585             get;
586             set;
587         }
588
589         /// <summary>
590         ///   The compression method used on each entry added to the ZipOutputStream.
591         /// </summary>
592         public Ionic.Zip.CompressionMethod CompressionMethod
593         {
594             get;
595             set;
596         }
597
598
599         /// <summary>
600         ///   A comment attached to the zip archive.
601         /// </summary>
602         ///
603         /// <remarks>
604         ///
605         /// <para>
606         ///   The application sets this property to specify a comment to be embedded
607         ///   into the generated zip archive.
608         /// </para>
609         ///
610         /// <para>
611         ///   According to <see
612         ///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's
613         ///   zip specification</see>, the comment is not encrypted, even if there is a
614         ///   password set on the zip file.
615         /// </para>
616         ///
617         /// <para>
618         ///   The specification does not describe how to indicate the encoding used
619         ///   on a comment string. Many "compliant" zip tools and libraries use
620         ///   IBM437 as the code page for comments; DotNetZip, too, follows that
621         ///   practice.  On the other hand, there are situations where you want a
622         ///   Comment to be encoded with something else, for example using code page
623         ///   950 "Big-5 Chinese". To fill that need, DotNetZip will encode the
624         ///   comment following the same procedure it follows for encoding
625         ///   filenames: (a) if <see cref="AlternateEncodingUsage"/> is
626         ///   <c>Never</c>, it uses the default encoding (IBM437). (b) if <see
627         ///   cref="AlternateEncodingUsage"/> is <c>Always</c>, it always uses the
628         ///   alternate encoding (<see cref="AlternateEncoding"/>). (c) if <see
629         ///   cref="AlternateEncodingUsage"/> is <c>AsNecessary</c>, it uses the
630         ///   alternate encoding only if the default encoding is not sufficient for
631         ///   encoding the comment - in other words if decoding the result does not
632         ///   produce the original string.  This decision is taken at the time of
633         ///   the call to <c>ZipFile.Save()</c>.
634         /// </para>
635         ///
636         /// </remarks>
637         public string Comment
638         {
639             get { return _comment; }
640             set
641             {
642                 if (_disposed)
643                 {
644                     _exceptionPending = true;
645                     throw new System.InvalidOperationException("The stream has been closed.");
646                 }
647                 _comment = value;
648             }
649         }
650
651
652
653         /// <summary>
654         ///   Specify whether to use ZIP64 extensions when saving a zip archive.
655         /// </summary>
656         ///
657         /// <remarks>
658         /// <para>
659         ///   The default value for the property is <see
660         ///   cref="Zip64Option.Never"/>. <see cref="Zip64Option.AsNecessary"/> is
661         ///   safest, in the sense that you will not get an Exception if a
662         ///   pre-ZIP64 limit is exceeded.
663         /// </para>
664         ///
665         /// <para>
666         ///   You must set this property before calling <c>Write()</c>.
667         /// </para>
668         ///
669         /// </remarks>
670         public Zip64Option EnableZip64
671         {
672             get
673             {
674                 return _zip64;
675             }
676             set
677             {
678                 if (_disposed)
679                 {
680                     _exceptionPending = true;
681                     throw new System.InvalidOperationException("The stream has been closed.");
682                 }
683                 _zip64 = value;
684             }
685         }
686
687
688         /// <summary>
689         ///   Indicates whether ZIP64 extensions were used when saving the zip archive.
690         /// </summary>
691         ///
692         /// <remarks>
693         ///   The value is defined only after the <c>ZipOutputStream</c> has been closed.
694         /// </remarks>
695         public bool OutputUsedZip64
696         {
697             get
698             {
699                 return _anyEntriesUsedZip64 || _directoryNeededZip64;
700             }
701         }
702
703
704         /// <summary>
705         ///   Whether the ZipOutputStream should use case-insensitive comparisons when
706         ///   checking for uniqueness of zip entries.
707         /// </summary>
708         ///
709         /// <remarks>
710         ///   <para>
711         ///   Though the zip specification doesn't prohibit zipfiles with duplicate
712         ///   entries, Sane zip files have no duplicates, and the DotNetZip library
713         ///   cannot create zip files with duplicate entries. If an application attempts
714         ///   to call <see cref="PutNextEntry(String)"/> with a name that duplicates one
715         ///   already used within the archive, the library will throw an Exception.
716         ///   </para>
717         ///   <para>
718         ///   This property allows the application to specify whether the
719         ///   ZipOutputStream instance considers ordinal case when checking for
720         ///   uniqueness of zip entries.
721         ///   </para>
722         /// </remarks>
723         public bool IgnoreCase
724         {
725           get
726           {
727               return !_DontIgnoreCase;
728           }
729
730           set
731           {
732               _DontIgnoreCase = !value;
733           }
734
735         }
736
737
738         /// <summary>
739         ///   Indicates whether to encode entry filenames and entry comments using
740         ///   Unicode (UTF-8).
741         /// </summary>
742         ///
743         /// <remarks>
744         /// <para>
745         ///   <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">The
746         ///   PKWare zip specification</see> provides for encoding file names and file
747         ///   comments in either the IBM437 code page, or in UTF-8.  This flag selects
748         ///   the encoding according to that specification.  By default, this flag is
749         ///   false, and filenames and comments are encoded into the zip file in the
750         ///   IBM437 codepage.  Setting this flag to true will specify that filenames
751         ///   and comments that cannot be encoded with IBM437 will be encoded with
752         ///   UTF-8.
753         /// </para>
754         ///
755         /// <para>
756         ///   Zip files created with strict adherence to the PKWare specification with
757         ///   respect to UTF-8 encoding can contain entries with filenames containing
758         ///   any combination of Unicode characters, including the full range of
759         ///   characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other
760         ///   alphabets.  However, because at this time, the UTF-8 portion of the PKWare
761         ///   specification is not broadly supported by other zip libraries and
762         ///   utilities, such zip files may not be readable by your favorite zip tool or
763         ///   archiver. In other words, interoperability will decrease if you set this
764         ///   flag to true.
765         /// </para>
766         ///
767         /// <para>
768         ///   In particular, Zip files created with strict adherence to the PKWare
769         ///   specification with respect to UTF-8 encoding will not work well with
770         ///   Explorer in Windows XP or Windows Vista, because Windows compressed
771         ///   folders, as far as I know, do not support UTF-8 in zip files.  Vista can
772         ///   read the zip files, but shows the filenames incorrectly. Unpacking from
773         ///   Windows Vista Explorer will result in filenames that have rubbish
774         ///   characters in place of the high-order UTF-8 bytes.
775         /// </para>
776         ///
777         /// <para>
778         ///   Also, zip files that use UTF-8 encoding will not work well with Java
779         ///   applications that use the java.util.zip classes, as of v5.0 of the Java
780         ///   runtime. The Java runtime does not correctly implement the PKWare
781         ///   specification in this regard.
782         /// </para>
783         ///
784         /// <para>
785         ///   As a result, we have the unfortunate situation that "correct" behavior by
786         ///   the DotNetZip library with regard to Unicode encoding of filenames during
787         ///   zip creation will result in zip files that are readable by strictly
788         ///   compliant and current tools (for example the most recent release of the
789         ///   commercial WinZip tool); but these zip files will not be readable by
790         ///   various other tools or libraries, including Windows Explorer.
791         /// </para>
792         ///
793         /// <para>
794         ///   The DotNetZip library can read and write zip files with UTF8-encoded
795         ///   entries, according to the PKware spec.  If you use DotNetZip for both
796         ///   creating and reading the zip file, and you use UTF-8, there will be no
797         ///   loss of information in the filenames. For example, using a self-extractor
798         ///   created by this library will allow you to unpack files correctly with no
799         ///   loss of information in the filenames.
800         /// </para>
801         ///
802         /// <para>
803         ///   If you do not set this flag, it will remain false.  If this flag is false,
804         ///   the <c>ZipOutputStream</c> will encode all filenames and comments using
805         ///   the IBM437 codepage.  This can cause "loss of information" on some
806         ///   filenames, but the resulting zipfile will be more interoperable with other
807         ///   utilities. As an example of the loss of information, diacritics can be
808         ///   lost.  The o-tilde character will be down-coded to plain o.  The c with a
809         ///   cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c.
810         ///   Likewise, the O-stroke character (Unicode 248), used in Danish and
811         ///   Norwegian, will be down-coded to plain o. Chinese characters cannot be
812         ///   represented in codepage IBM437; when using the default encoding, Chinese
813         ///   characters in filenames will be represented as ?. These are all examples
814         ///   of "information loss".
815         /// </para>
816         ///
817         /// <para>
818         ///   The loss of information associated to the use of the IBM437 encoding is
819         ///   inconvenient, and can also lead to runtime errors. For example, using
820         ///   IBM437, any sequence of 4 Chinese characters will be encoded as ????.  If
821         ///   your application creates a <c>ZipOutputStream</c>, does not set the
822         ///   encoding, then adds two files, each with names of four Chinese characters
823         ///   each, this will result in a duplicate filename exception.  In the case
824         ///   where you add a single file with a name containing four Chinese
825         ///   characters, the zipfile will save properly, but extracting that file
826         ///   later, with any zip tool, will result in an error, because the question
827         ///   mark is not legal for use within filenames on Windows.  These are just a
828         ///   few examples of the problems associated to loss of information.
829         /// </para>
830         ///
831         /// <para>
832         ///   This flag is independent of the encoding of the content within the entries
833         ///   in the zip file. Think of the zip file as a container - it supports an
834         ///   encoding.  Within the container are other "containers" - the file entries
835         ///   themselves.  The encoding within those entries is independent of the
836         ///   encoding of the zip archive container for those entries.
837         /// </para>
838         ///
839         /// <para>
840         ///   Rather than specify the encoding in a binary fashion using this flag, an
841         ///   application can specify an arbitrary encoding via the <see
842         ///   cref="ProvisionalAlternateEncoding"/> property.  Setting the encoding
843         ///   explicitly when creating zip archives will result in non-compliant zip
844         ///   files that, curiously, are fairly interoperable.  The challenge is, the
845         ///   PKWare specification does not provide for a way to specify that an entry
846         ///   in a zip archive uses a code page that is neither IBM437 nor UTF-8.
847         ///   Therefore if you set the encoding explicitly when creating a zip archive,
848         ///   you must take care upon reading the zip archive to use the same code page.
849         ///   If you get it wrong, the behavior is undefined and may result in incorrect
850         ///   filenames, exceptions, stomach upset, hair loss, and acne.
851         /// </para>
852         /// </remarks>
853         /// <seealso cref="ProvisionalAlternateEncoding"/>
854         [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Use AlternateEncoding and AlternateEncodingUsage instead.")]
855         public bool UseUnicodeAsNecessary
856         {
857             get
858             {
859                 return (_alternateEncoding == System.Text.Encoding.UTF8) &&
860                     (AlternateEncodingUsage == ZipOption.AsNecessary);
861             }
862             set
863             {
864                 if (value)
865                 {
866                     _alternateEncoding = System.Text.Encoding.UTF8;
867                     _alternateEncodingUsage = ZipOption.AsNecessary;
868
869                 }
870                 else
871                 {
872                     _alternateEncoding = Ionic.Zip.ZipOutputStream.DefaultEncoding;
873                     _alternateEncodingUsage = ZipOption.Never;
874                 }
875             }
876         }
877
878
879         /// <summary>
880         ///   The text encoding to use when emitting entries into the zip archive, for
881         ///   those entries whose filenames or comments cannot be encoded with the
882         ///   default (IBM437) encoding.
883         /// </summary>
884         ///
885         /// <remarks>
886         /// <para>
887         ///   In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its
888         ///   zip specification</see>, PKWare describes two options for encoding
889         ///   filenames and comments: using IBM437 or UTF-8.  But, some archiving tools
890         ///   or libraries do not follow the specification, and instead encode
891         ///   characters using the system default code page.  For example, WinRAR when
892         ///   run on a machine in Shanghai may encode filenames with the Big-5 Chinese
893         ///   (950) code page.  This behavior is contrary to the Zip specification, but
894         ///   it occurs anyway.
895         /// </para>
896         ///
897         /// <para>
898         ///   When using DotNetZip to write zip archives that will be read by one of
899         ///   these other archivers, set this property to specify the code page to use
900         ///   when encoding the <see cref="ZipEntry.FileName"/> and <see
901         ///   cref="ZipEntry.Comment"/> for each <c>ZipEntry</c> in the zip file, for
902         ///   values that cannot be encoded with the default codepage for zip files,
903         ///   IBM437.  This is why this property is "provisional".  In all cases, IBM437
904         ///   is used where possible, in other words, where no loss of data would
905         ///   result. It is possible, therefore, to have a given entry with a
906         ///   <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with the
907         ///   specified "provisional" codepage.
908         /// </para>
909         ///
910         /// <para>
911         ///   Be aware that a zip file created after you've explicitly set the
912         ///   <c>ProvisionalAlternateEncoding</c> property to a value other than
913         ///   IBM437 may not be compliant to the PKWare specification, and may not be
914         ///   readable by compliant archivers.  On the other hand, many (most?)
915         ///   archivers are non-compliant and can read zip files created in arbitrary
916         ///   code pages.  The trick is to use or specify the proper codepage when
917         ///   reading the zip.
918         /// </para>
919         ///
920         /// <para>
921         ///   When creating a zip archive using this library, it is possible to change
922         ///   the value of <c>ProvisionalAlternateEncoding</c> between each entry you
923         ///   add, and between adding entries and the call to <c>Close()</c>. Don't do
924         ///   this. It will likely result in a zipfile that is not readable.  For best
925         ///   interoperability, either leave <c>ProvisionalAlternateEncoding</c>
926         ///   alone, or specify it only once, before adding any entries to the
927         ///   <c>ZipOutputStream</c> instance.  There is one exception to this
928         ///   recommendation, described later.
929         /// </para>
930         ///
931         /// <para>
932         ///   When using an arbitrary, non-UTF8 code page for encoding, there is no
933         ///   standard way for the creator application - whether DotNetZip, WinZip,
934         ///   WinRar, or something else - to formally specify in the zip file which
935         ///   codepage has been used for the entries. As a result, readers of zip files
936         ///   are not able to inspect the zip file and determine the codepage that was
937         ///   used for the entries contained within it.  It is left to the application
938         ///   or user to determine the necessary codepage when reading zip files encoded
939         ///   this way.  If you use an incorrect codepage when reading a zipfile, you
940         ///   will get entries with filenames that are incorrect, and the incorrect
941         ///   filenames may even contain characters that are not legal for use within
942         ///   filenames in Windows. Extracting entries with illegal characters in the
943         ///   filenames will lead to exceptions. It's too bad, but this is just the way
944         ///   things are with code pages in zip files. Caveat Emptor.
945         /// </para>
946         ///
947         /// <para>
948         ///   One possible approach for specifying the code page for a given zip file is
949         ///   to describe the code page in a human-readable form in the Zip comment. For
950         ///   example, the comment may read "Entries in this archive are encoded in the
951         ///   Big5 code page".  For maximum interoperability, the zip comment in this
952         ///   case should be encoded in the default, IBM437 code page.  In this case,
953         ///   the zip comment is encoded using a different page than the filenames.  To
954         ///   do this, Specify <c>ProvisionalAlternateEncoding</c> to your desired
955         ///   region-specific code page, once before adding any entries, and then set
956         ///   the <see cref="Comment"/> property and reset
957         ///   <c>ProvisionalAlternateEncoding</c> to IBM437 before calling <c>Close()</c>.
958         /// </para>
959         /// </remarks>
960         [Obsolete("use AlternateEncoding and AlternateEncodingUsage instead.")]
961         public System.Text.Encoding ProvisionalAlternateEncoding
962         {
963             get
964             {
965                 if (_alternateEncodingUsage == ZipOption.AsNecessary)
966                     return _alternateEncoding;
967                 return null;
968             }
969             set
970             {
971                 _alternateEncoding = value;
972                 _alternateEncodingUsage = ZipOption.AsNecessary;
973             }
974         }
975
976         /// <summary>
977         ///   A Text Encoding to use when encoding the filenames and comments for
978         ///   all the ZipEntry items, during a ZipFile.Save() operation.
979         /// </summary>
980         /// <remarks>
981         ///   <para>
982         ///     Whether the encoding specified here is used during the save depends
983         ///     on <see cref="AlternateEncodingUsage"/>.
984         ///   </para>
985         /// </remarks>
986         public System.Text.Encoding AlternateEncoding
987         {
988             get
989             {
990                 return _alternateEncoding;
991             }
992             set
993             {
994                 _alternateEncoding = value;
995             }
996         }
997
998         /// <summary>
999         ///   A flag that tells if and when this instance should apply
1000         ///   AlternateEncoding to encode the filenames and comments associated to
1001         ///   of ZipEntry objects contained within this instance.
1002         /// </summary>
1003         public ZipOption AlternateEncodingUsage
1004         {
1005             get
1006             {
1007                 return _alternateEncodingUsage;
1008             }
1009             set
1010             {
1011                 _alternateEncodingUsage = value;
1012             }
1013         }
1014
1015         /// <summary>
1016         /// The default text encoding used in zip archives.  It is numeric 437, also
1017         /// known as IBM437.
1018         /// </summary>
1019         /// <seealso cref="Ionic.Zip.ZipFile.ProvisionalAlternateEncoding"/>
1020         internal static System.Text.Encoding DefaultEncoding
1021         {
1022             get
1023             {
1024                 return System.Text.Encoding.GetEncoding("IBM437");
1025             }
1026         }
1027
1028
1029 #if !NETCF
1030         /// <summary>
1031         ///   The size threshold for an entry, above which a parallel deflate is used.
1032         /// </summary>
1033         ///
1034         /// <remarks>
1035         ///
1036         ///   <para>
1037         ///     DotNetZip will use multiple threads to compress any ZipEntry, when
1038         ///     the <c>CompressionMethod</c> is Deflate, and if the entry is
1039         ///     larger than the given size.  Zero means "always use parallel
1040         ///     deflate", while -1 means "never use parallel deflate".
1041         ///   </para>
1042         ///
1043         ///   <para>
1044         ///     If the entry size cannot be known before compression, as with any entry
1045         ///     added via a ZipOutputStream, then Parallel deflate will never be
1046         ///     performed, unless the value of this property is zero.
1047         ///   </para>
1048         ///
1049         ///   <para>
1050         ///     A parallel deflate operations will speed up the compression of
1051         ///     large files, on computers with multiple CPUs or multiple CPU
1052         ///     cores.  For files above 1mb, on a dual core or dual-cpu (2p)
1053         ///     machine, the time required to compress the file can be 70% of the
1054         ///     single-threaded deflate.  For very large files on 4p machines the
1055         ///     compression can be done in 30% of the normal time.  The downside
1056         ///     is that parallel deflate consumes extra memory during the deflate,
1057         ///     and the deflation is slightly less effective.
1058         ///   </para>
1059         ///
1060         ///   <para>
1061         ///     Parallel deflate tends to not be as effective as single-threaded deflate
1062         ///     because the original data stream is split into multiple independent
1063         ///     buffers, each of which is compressed in parallel.  But because they are
1064         ///     treated independently, there is no opportunity to share compression
1065         ///     dictionaries, and additional framing bytes must be added to the output
1066         ///     stream.  For that reason, a deflated stream may be slightly larger when
1067         ///     compressed using parallel deflate, as compared to a traditional
1068         ///     single-threaded deflate. For files of about 512k, the increase over the
1069         ///     normal deflate is as much as 5% of the total compressed size. For larger
1070         ///     files, the difference can be as small as 0.1%.
1071         ///   </para>
1072         ///
1073         ///   <para>
1074         ///     Multi-threaded compression does not give as much an advantage when using
1075         ///     Encryption. This is primarily because encryption tends to slow down
1076         ///     the entire pipeline. Also, multi-threaded compression gives less of an
1077         ///     advantage when using lower compression levels, for example <see
1078         ///     cref="Ionic.Zlib.CompressionLevel.BestSpeed"/>.  You may have to perform
1079         ///     some tests to determine the best approach for your situation.
1080         ///   </para>
1081         ///
1082         ///   <para>
1083         ///     The default value for this property is -1, which means parallel
1084         ///     compression will not be performed unless you set it to zero.
1085         ///   </para>
1086         ///
1087         /// </remarks>
1088         public long ParallelDeflateThreshold
1089         {
1090             set
1091             {
1092                 if ((value != 0) && (value != -1) && (value < 64 * 1024))
1093                     throw new ArgumentOutOfRangeException("value must be greater than 64k, or 0, or -1");
1094                 _ParallelDeflateThreshold = value;
1095             }
1096             get
1097             {
1098                 return _ParallelDeflateThreshold;
1099             }
1100         }
1101
1102
1103         /// <summary>
1104         ///   The maximum number of buffer pairs to use when performing
1105         ///   parallel compression.
1106         /// </summary>
1107         ///
1108         /// <remarks>
1109         /// <para>
1110         ///   This property sets an upper limit on the number of memory
1111         ///   buffer pairs to create when performing parallel
1112         ///   compression.  The implementation of the parallel
1113         ///   compression stream allocates multiple buffers to
1114         ///   facilitate parallel compression.  As each buffer fills up,
1115         ///   the stream uses <see
1116         ///   cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)">
1117         ///   ThreadPool.QueueUserWorkItem()</see> to compress those
1118         ///   buffers in a background threadpool thread. After a buffer
1119         ///   is compressed, it is re-ordered and written to the output
1120         ///   stream.
1121         /// </para>
1122         ///
1123         /// <para>
1124         ///   A higher number of buffer pairs enables a higher degree of
1125         ///   parallelism, which tends to increase the speed of compression on
1126         ///   multi-cpu computers.  On the other hand, a higher number of buffer
1127         ///   pairs also implies a larger memory consumption, more active worker
1128         ///   threads, and a higher cpu utilization for any compression. This
1129         ///   property enables the application to limit its memory consumption and
1130         ///   CPU utilization behavior depending on requirements.
1131         /// </para>
1132         ///
1133         /// <para>
1134         ///   For each compression "task" that occurs in parallel, there are 2
1135         ///   buffers allocated: one for input and one for output.  This property
1136         ///   sets a limit for the number of pairs.  The total amount of storage
1137         ///   space allocated for buffering will then be (N*S*2), where N is the
1138         ///   number of buffer pairs, S is the size of each buffer (<see
1139         ///   cref="CodecBufferSize"/>).  By default, DotNetZip allocates 4 buffer
1140         ///   pairs per CPU core, so if your machine has 4 cores, and you retain
1141         ///   the default buffer size of 128k, then the
1142         ///   ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer
1143         ///   memory in total, or 4mb, in blocks of 128kb.  If you then set this
1144         ///   property to 8, then the number will be 8 * 2 * 128kb of buffer
1145         ///   memory, or 2mb.
1146         /// </para>
1147         ///
1148         /// <para>
1149         ///   CPU utilization will also go up with additional buffers, because a
1150         ///   larger number of buffer pairs allows a larger number of background
1151         ///   threads to compress in parallel. If you find that parallel
1152         ///   compression is consuming too much memory or CPU, you can adjust this
1153         ///   value downward.
1154         /// </para>
1155         ///
1156         /// <para>
1157         ///   The default value is 16. Different values may deliver better or
1158         ///   worse results, depending on your priorities and the dynamic
1159         ///   performance characteristics of your storage and compute resources.
1160         /// </para>
1161         ///
1162         /// <para>
1163         ///   This property is not the number of buffer pairs to use; it is an
1164         ///   upper limit. An illustration: Suppose you have an application that
1165         ///   uses the default value of this property (which is 16), and it runs
1166         ///   on a machine with 2 CPU cores. In that case, DotNetZip will allocate
1167         ///   4 buffer pairs per CPU core, for a total of 8 pairs.  The upper
1168         ///   limit specified by this property has no effect.
1169         /// </para>
1170         ///
1171         /// <para>
1172         ///   The application can set this value at any time, but it is
1173         ///   effective only if set before calling
1174         ///   <c>ZipOutputStream.Write()</c> for the first time.
1175         /// </para>
1176         /// </remarks>
1177         ///
1178         /// <seealso cref="ParallelDeflateThreshold"/>
1179         ///
1180         public int ParallelDeflateMaxBufferPairs
1181         {
1182             get
1183             {
1184                 return _maxBufferPairs;
1185             }
1186             set
1187             {
1188                 if (value < 4)
1189                     throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs",
1190                                                 "Value must be 4 or greater.");
1191                 _maxBufferPairs = value;
1192             }
1193         }
1194 #endif
1195
1196
1197         private void InsureUniqueEntry(ZipEntry ze1)
1198         {
1199             if (_entriesWritten.ContainsKey(ze1.FileName))
1200             {
1201                 _exceptionPending = true;
1202                 throw new ArgumentException(String.Format("The entry '{0}' already exists in the zip archive.", ze1.FileName));
1203             }
1204         }
1205
1206
1207         internal Stream OutputStream
1208         {
1209             get
1210             {
1211                 return _outputStream;
1212             }
1213         }
1214
1215         internal String Name
1216         {
1217             get
1218             {
1219                 return _name;
1220             }
1221         }
1222
1223         /// <summary>
1224         ///   Returns true if an entry by the given name has already been written
1225         ///   to the ZipOutputStream.
1226         /// </summary>
1227         ///
1228         /// <param name="name">
1229         ///   The name of the entry to scan for.
1230         /// </param>
1231         ///
1232         /// <returns>
1233         /// true if an entry by the given name has already been written.
1234         /// </returns>
1235         public bool ContainsEntry(string name)
1236         {
1237             return _entriesWritten.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name));
1238         }
1239
1240
1241         /// <summary>
1242         ///   Write the data from the buffer to the stream.
1243         /// </summary>
1244         ///
1245         /// <remarks>
1246         ///   As the application writes data into this stream, the data may be
1247         ///   compressed and encrypted before being written out to the underlying
1248         ///   stream, depending on the settings of the <see cref="CompressionLevel"/>
1249         ///   and the <see cref="Encryption"/> properties.
1250         /// </remarks>
1251         ///
1252         /// <param name="buffer">The buffer holding data to write to the stream.</param>
1253         /// <param name="offset">the offset within that data array to find the first byte to write.</param>
1254         /// <param name="count">the number of bytes to write.</param>
1255         public override void Write(byte[] buffer, int offset, int count)
1256         {
1257             if (_disposed)
1258             {
1259                 _exceptionPending = true;
1260                 throw new System.InvalidOperationException("The stream has been closed.");
1261             }
1262
1263             if (buffer==null)
1264             {
1265                 _exceptionPending = true;
1266                 throw new System.ArgumentNullException("buffer");
1267             }
1268
1269             if (_currentEntry == null)
1270             {
1271                 _exceptionPending = true;
1272                 throw new System.InvalidOperationException("You must call PutNextEntry() before calling Write().");
1273             }
1274
1275             if (_currentEntry.IsDirectory)
1276             {
1277                 _exceptionPending = true;
1278                 throw new System.InvalidOperationException("You cannot Write() data for an entry that is a directory.");
1279             }
1280
1281             if (_needToWriteEntryHeader)
1282                 _InitiateCurrentEntry(false);
1283
1284             if (count != 0)
1285                 _entryOutputStream.Write(buffer, offset, count);
1286         }
1287
1288
1289
1290         /// <summary>
1291         ///   Specify the name of the next entry that will be written to the zip file.
1292         /// </summary>
1293         ///
1294         /// <remarks>
1295         /// <para>
1296         ///   Call this method just before calling <see cref="Write(byte[], int, int)"/>, to
1297         ///   specify the name of the entry that the next set of bytes written to
1298         ///   the <c>ZipOutputStream</c> belongs to. All subsequent calls to <c>Write</c>,
1299         ///   until the next call to <c>PutNextEntry</c>,
1300         ///   will be inserted into the named entry in the zip file.
1301         /// </para>
1302         ///
1303         /// <para>
1304         ///   If the <paramref name="entryName"/> used in <c>PutNextEntry()</c> ends in
1305         ///   a slash, then the entry added is marked as a directory. Because directory
1306         ///   entries do not contain data, a call to <c>Write()</c>, before an
1307         ///   intervening additional call to <c>PutNextEntry()</c>, will throw an
1308         ///   exception.
1309         /// </para>
1310         ///
1311         /// <para>
1312         ///   If you don't call <c>Write()</c> between two calls to
1313         ///   <c>PutNextEntry()</c>, the first entry is inserted into the zip file as a
1314         ///   file of zero size.  This may be what you want.
1315         /// </para>
1316         ///
1317         /// <para>
1318         ///   Because <c>PutNextEntry()</c> closes out the prior entry, if any, this
1319         ///   method may throw if there is a problem with the prior entry.
1320         /// </para>
1321         ///
1322         /// <para>
1323         ///   This method returns the <c>ZipEntry</c>.  You can modify public properties
1324         ///   on the <c>ZipEntry</c>, such as <see cref="ZipEntry.Encryption"/>, <see
1325         ///   cref="ZipEntry.Password"/>, and so on, until the first call to
1326         ///   <c>ZipOutputStream.Write()</c>, or until the next call to
1327         ///   <c>PutNextEntry()</c>.  If you modify the <c>ZipEntry</c> <em>after</em>
1328         ///   having called <c>Write()</c>, you may get a runtime exception, or you may
1329         ///   silently get an invalid zip archive.
1330         /// </para>
1331         ///
1332         /// </remarks>
1333         ///
1334         /// <example>
1335         ///
1336         ///   This example shows how to create a zip file, using the
1337         ///   <c>ZipOutputStream</c> class.
1338         ///
1339         /// <code>
1340         /// private void Zipup()
1341         /// {
1342         ///     using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
1343         ///     {
1344         ///         using (var output= new ZipOutputStream(fs))
1345         ///         {
1346         ///             output.Password = "VerySecret!";
1347         ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
1348         ///             output.PutNextEntry("entry1.txt");
1349         ///             byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1.");
1350         ///             output.Write(buffer,0,buffer.Length);
1351         ///             output.PutNextEntry("entry2.txt");  // this will be zero length
1352         ///             output.PutNextEntry("entry3.txt");
1353         ///             buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3.");
1354         ///             output.Write(buffer,0,buffer.Length);
1355         ///         }
1356         ///     }
1357         /// }
1358         /// </code>
1359         /// </example>
1360         ///
1361         /// <param name="entryName">
1362         ///   The name of the entry to be added, including any path to be used
1363         ///   within the zip file.
1364         /// </param>
1365         ///
1366         /// <returns>
1367         ///   The ZipEntry created.
1368         /// </returns>
1369         ///
1370         public ZipEntry PutNextEntry(String entryName)
1371         {
1372             if (String.IsNullOrEmpty(entryName))
1373                 throw new ArgumentNullException("entryName");
1374
1375             if (_disposed)
1376             {
1377                 _exceptionPending = true;
1378                 throw new System.InvalidOperationException("The stream has been closed.");
1379             }
1380
1381             _FinishCurrentEntry();
1382             _currentEntry = ZipEntry.CreateForZipOutputStream(entryName);
1383             _currentEntry._container = new ZipContainer(this);
1384             _currentEntry._BitField |= 0x0008;  // workitem 8932
1385             _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now);
1386             _currentEntry.CompressionLevel = this.CompressionLevel;
1387             _currentEntry.CompressionMethod = this.CompressionMethod;
1388             _currentEntry.Password = _password; // workitem 13909
1389             _currentEntry.Encryption = this.Encryption;
1390             // workitem 12634
1391             _currentEntry.AlternateEncoding = this.AlternateEncoding;
1392             _currentEntry.AlternateEncodingUsage = this.AlternateEncodingUsage;
1393
1394             if (entryName.EndsWith("/"))  _currentEntry.MarkAsDirectory();
1395
1396             _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0);
1397             _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0);
1398             InsureUniqueEntry(_currentEntry);
1399             _needToWriteEntryHeader = true;
1400
1401             return _currentEntry;
1402         }
1403
1404
1405
1406         private void _InitiateCurrentEntry(bool finishing)
1407         {
1408             // If finishing==true, this means we're initiating the entry at the time of
1409             // Close() or PutNextEntry().  If this happens, it means no data was written
1410             // for the entry - Write() was never called.  (The usual case us to call
1411             // _InitiateCurrentEntry(bool) from within Write().)  If finishing==true,
1412             // the entry could be either a zero-byte file or a directory.
1413
1414             _entriesWritten.Add(_currentEntry.FileName,_currentEntry);
1415             _entryCount++; // could use _entriesWritten.Count, but I don't want to incur
1416             // the cost.
1417
1418             if (_entryCount > 65534 && _zip64 == Zip64Option.Never)
1419             {
1420                 _exceptionPending = true;
1421                 throw new System.InvalidOperationException("Too many entries. Consider setting ZipOutputStream.EnableZip64.");
1422             }
1423
1424             // Write out the header.
1425             //
1426             // If finishing, and encryption is in use, then we don't want to emit the
1427             // normal encryption header.  Signal that with a cycle=99 to turn off
1428             // encryption for zero-byte entries or directories.
1429             //
1430             // If finishing, then we know the stream length is zero.  Else, unknown
1431             // stream length.  Passing stream length == 0 allows an optimization so as
1432             // not to setup an encryption or deflation stream, when stream length is
1433             // zero.
1434
1435             _currentEntry.WriteHeader(_outputStream, finishing ? 99 : 0);
1436             _currentEntry.StoreRelativeOffset();
1437
1438             if (!_currentEntry.IsDirectory)
1439             {
1440                 _currentEntry.WriteSecurityMetadata(_outputStream);
1441                 _currentEntry.PrepOutputStream(_outputStream,
1442                                                finishing ? 0 : -1,
1443                                                out _outputCounter,
1444                                                out _encryptor,
1445                                                out _deflater,
1446                                                out _entryOutputStream);
1447             }
1448             _needToWriteEntryHeader = false;
1449         }
1450
1451
1452
1453         private void _FinishCurrentEntry()
1454         {
1455             if (_currentEntry != null)
1456             {
1457                 if (_needToWriteEntryHeader)
1458                     _InitiateCurrentEntry(true); // an empty entry - no writes
1459
1460                 _currentEntry.FinishOutputStream(_outputStream, _outputCounter, _encryptor, _deflater, _entryOutputStream);
1461                 _currentEntry.PostProcessOutput(_outputStream);
1462                 // workitem 12964
1463                 if (_currentEntry.OutputUsedZip64!=null)
1464                     _anyEntriesUsedZip64 |= _currentEntry.OutputUsedZip64.Value;
1465
1466                 // reset all the streams
1467                 _outputCounter = null; _encryptor = _deflater = null; _entryOutputStream = null;
1468             }
1469         }
1470
1471
1472
1473         /// <summary>
1474         /// Dispose the stream
1475         /// </summary>
1476         ///
1477         /// <remarks>
1478         /// <para>
1479         ///   This method writes the Zip Central directory, then closes the stream.  The
1480         ///   application must call Dispose() (or Close) in order to produce a valid zip file.
1481         /// </para>
1482         ///
1483         /// <para>
1484         ///   Typically the application will call <c>Dispose()</c> implicitly, via a <c>using</c>
1485         ///   statement in C#, or a <c>Using</c> statement in VB.
1486         /// </para>
1487         ///
1488         /// </remarks>
1489         ///
1490         /// <param name="disposing">set this to true, always.</param>
1491         protected override void Dispose(bool disposing)
1492         {
1493             if (_disposed) return;
1494
1495             if (disposing) // not called from finalizer
1496             {
1497                 // handle pending exceptions
1498                 if (!_exceptionPending)
1499                 {
1500                     _FinishCurrentEntry();
1501                     _directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(_outputStream,
1502                                                                                      _entriesWritten.Values,
1503                                                                                      1, // _numberOfSegmentsForMostRecentSave,
1504                                                                                      _zip64,
1505                                                                                      Comment,
1506                                                                                      new ZipContainer(this));
1507                     Stream wrappedStream = null;
1508                     CountingStream cs = _outputStream as CountingStream;
1509                     if (cs != null)
1510                     {
1511                         wrappedStream = cs.WrappedStream;
1512 #if NETCF
1513                     cs.Close();
1514 #else
1515                         cs.Dispose();
1516 #endif
1517                     }
1518                     else
1519                     {
1520                         wrappedStream = _outputStream;
1521                     }
1522
1523                     if (!_leaveUnderlyingStreamOpen)
1524                     {
1525 #if NETCF
1526                     wrappedStream.Close();
1527 #else
1528                         wrappedStream.Dispose();
1529 #endif
1530                     }
1531                     _outputStream = null;
1532                 }
1533             }
1534             _disposed = true;
1535         }
1536
1537
1538
1539         /// <summary>
1540         /// Always returns false.
1541         /// </summary>
1542         public override bool CanRead { get { return false; } }
1543
1544         /// <summary>
1545         /// Always returns false.
1546         /// </summary>
1547         public override bool CanSeek { get { return false; } }
1548
1549         /// <summary>
1550         /// Always returns true.
1551         /// </summary>
1552         public override bool CanWrite { get { return true; } }
1553
1554         /// <summary>
1555         /// Always returns a NotSupportedException.
1556         /// </summary>
1557         public override long Length { get { throw new NotSupportedException(); } }
1558
1559         /// <summary>
1560         /// Setting this property always returns a NotSupportedException. Getting it
1561         /// returns the value of the Position on the underlying stream.
1562         /// </summary>
1563         public override long Position
1564         {
1565             get { return _outputStream.Position; }
1566             set { throw new NotSupportedException(); }
1567         }
1568
1569         /// <summary>
1570         /// This is a no-op.
1571         /// </summary>
1572         public override void Flush() { }
1573
1574         /// <summary>
1575         /// This method always throws a NotSupportedException.
1576         /// </summary>
1577         /// <param name="buffer">ignored</param>
1578         /// <param name="offset">ignored</param>
1579         /// <param name="count">ignored</param>
1580         /// <returns>nothing</returns>
1581         public override int Read(byte[] buffer, int offset, int count)
1582         {
1583             throw new NotSupportedException("Read");
1584         }
1585
1586         /// <summary>
1587         /// This method always throws a NotSupportedException.
1588         /// </summary>
1589         /// <param name="offset">ignored</param>
1590         /// <param name="origin">ignored</param>
1591         /// <returns>nothing</returns>
1592         public override long Seek(long offset, SeekOrigin origin)
1593         {
1594             throw new NotSupportedException("Seek");
1595         }
1596
1597         /// <summary>
1598         /// This method always throws a NotSupportedException.
1599         /// </summary>
1600         /// <param name="value">ignored</param>
1601         public override void SetLength(long value)
1602         {
1603             throw new NotSupportedException();
1604         }
1605
1606
1607         private EncryptionAlgorithm _encryption;
1608         private ZipEntryTimestamp _timestamp;
1609         internal String _password;
1610         private String _comment;
1611         private Stream _outputStream;
1612         private ZipEntry _currentEntry;
1613         internal Zip64Option _zip64;
1614         private Dictionary<String, ZipEntry> _entriesWritten;
1615         private int _entryCount;
1616         private ZipOption _alternateEncodingUsage = ZipOption.Never;
1617         private System.Text.Encoding _alternateEncoding
1618             = System.Text.Encoding.GetEncoding("IBM437"); // default = IBM437
1619
1620         private bool _leaveUnderlyingStreamOpen;
1621         private bool _disposed;
1622         private bool _exceptionPending; // **see note below
1623         private bool _anyEntriesUsedZip64, _directoryNeededZip64;
1624         private CountingStream _outputCounter;
1625         private Stream _encryptor;
1626         private Stream _deflater;
1627         private Ionic.Crc.CrcCalculatorStream _entryOutputStream;
1628         private bool _needToWriteEntryHeader;
1629         private string _name;
1630         private bool _DontIgnoreCase;
1631 #if !NETCF
1632         internal Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater;
1633         private long _ParallelDeflateThreshold;
1634         private int _maxBufferPairs = 16;
1635 #endif
1636
1637         // **Note regarding exceptions:
1638
1639         // When ZipOutputStream is employed within a using clause, which
1640         // is the typical scenario, and an exception is thrown within
1641         // the scope of the using, Close()/Dispose() is invoked
1642         // implicitly before processing the initial exception.  In that
1643         // case, _exceptionPending is true, and we don't want to try to
1644         // write anything in the Close/Dispose logic.  Doing so can
1645         // cause additional exceptions that mask the original one. So,
1646         // the _exceptionPending flag is used to track that, and to
1647         // allow the original exception to be propagated to the
1648         // application without extra "noise."
1649
1650     }
1651
1652
1653
1654     internal class ZipContainer
1655     {
1656         private ZipFile _zf;
1657         private ZipOutputStream _zos;
1658         private ZipInputStream _zis;
1659
1660         public ZipContainer(Object o)
1661         {
1662             _zf = (o as ZipFile);
1663             _zos = (o as ZipOutputStream);
1664             _zis = (o as ZipInputStream);
1665         }
1666
1667         public ZipFile ZipFile
1668         {
1669             get { return _zf; }
1670         }
1671
1672         public ZipOutputStream ZipOutputStream
1673         {
1674             get { return _zos; }
1675         }
1676
1677         public string Name
1678         {
1679             get
1680             {
1681                 if (_zf != null) return _zf.Name;
1682                 if (_zis != null) throw new NotSupportedException();
1683                 return _zos.Name;
1684             }
1685         }
1686
1687         public string Password
1688         {
1689             get
1690             {
1691                 if (_zf != null) return _zf._Password;
1692                 if (_zis != null) return _zis._Password;
1693                 return _zos._password;
1694             }
1695         }
1696
1697         public Zip64Option Zip64
1698         {
1699             get
1700             {
1701                 if (_zf != null) return _zf._zip64;
1702                 if (_zis != null) throw new NotSupportedException();
1703                 return _zos._zip64;
1704             }
1705         }
1706
1707         public int BufferSize
1708         {
1709             get
1710             {
1711                 if (_zf != null) return _zf.BufferSize;
1712                 if (_zis != null) throw new NotSupportedException();
1713                 return 0;
1714             }
1715         }
1716
1717 #if !NETCF
1718         public Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater
1719         {
1720             get
1721             {
1722                 if (_zf != null) return _zf.ParallelDeflater;
1723                 if (_zis != null) return null;
1724                 return _zos.ParallelDeflater;
1725             }
1726             set
1727             {
1728                 if (_zf != null) _zf.ParallelDeflater = value;
1729                 else if (_zos != null) _zos.ParallelDeflater = value;
1730             }
1731         }
1732
1733         public long ParallelDeflateThreshold
1734         {
1735             get
1736             {
1737                 if (_zf != null) return _zf.ParallelDeflateThreshold;
1738                 return _zos.ParallelDeflateThreshold;
1739             }
1740         }
1741         public int ParallelDeflateMaxBufferPairs
1742         {
1743             get
1744             {
1745                 if (_zf != null) return _zf.ParallelDeflateMaxBufferPairs;
1746                 return _zos.ParallelDeflateMaxBufferPairs;
1747             }
1748         }
1749 #endif
1750
1751         public int CodecBufferSize
1752         {
1753             get
1754             {
1755                 if (_zf != null) return _zf.CodecBufferSize;
1756                 if (_zis != null) return _zis.CodecBufferSize;
1757                 return _zos.CodecBufferSize;
1758             }
1759         }
1760
1761         public Ionic.Zlib.CompressionStrategy Strategy
1762         {
1763             get
1764             {
1765                 if (_zf != null) return _zf.Strategy;
1766                 return _zos.Strategy;
1767             }
1768         }
1769
1770         public Zip64Option UseZip64WhenSaving
1771         {
1772             get
1773             {
1774                 if (_zf != null) return _zf.UseZip64WhenSaving;
1775                 return _zos.EnableZip64;
1776             }
1777         }
1778
1779         public System.Text.Encoding AlternateEncoding
1780         {
1781             get
1782             {
1783                 if (_zf != null) return _zf.AlternateEncoding;
1784                 if (_zos!=null) return _zos.AlternateEncoding;
1785                 return null;
1786             }
1787         }
1788         public System.Text.Encoding DefaultEncoding
1789         {
1790             get
1791             {
1792                 if (_zf != null) return ZipFile.DefaultEncoding;
1793                 if (_zos!=null) return ZipOutputStream.DefaultEncoding;
1794                 return null;
1795             }
1796         }
1797         public ZipOption AlternateEncodingUsage
1798         {
1799             get
1800             {
1801                 if (_zf != null) return _zf.AlternateEncodingUsage;
1802                 if (_zos!=null) return _zos.AlternateEncodingUsage;
1803                 return ZipOption.Never; // n/a
1804             }
1805         }
1806
1807         public Stream ReadStream
1808         {
1809             get
1810             {
1811                 if (_zf != null) return _zf.ReadStream;
1812                 return _zis.ReadStream;
1813             }
1814         }
1815     }
1816
1817 }