[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / System.IO.Compression / SharpCompress / Archive / Zip / ZipArchive.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text;
6 using SharpCompress.Common;
7 using SharpCompress.Common.Zip;
8 using SharpCompress.Common.Zip.Headers;
9 using SharpCompress.Compressor.Deflate;
10 using SharpCompress.Reader;
11 using SharpCompress.Reader.Zip;
12 using SharpCompress.Writer.Zip;
13
14 namespace SharpCompress.Archive.Zip
15 {
16     internal class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
17     {
18         private readonly SeekableZipHeaderFactory headerFactory;
19
20         /// <summary>
21         /// Gets or sets the compression level applied to files added to the archive,
22         /// if the compression method is set to deflate
23         /// </summary>
24         public CompressionLevel DeflateCompressionLevel { get; set; }
25
26 #if !PORTABLE && !NETFX_CORE
27         /// <summary>
28         /// Constructor expects a filepath to an existing file.
29         /// </summary>
30         /// <param name="filePath"></param>
31         /// <param name="password"></param>
32         public static ZipArchive Open(string filePath, string password = null)
33         {
34             return Open(filePath, Options.None, password);
35         }
36
37         /// <summary>
38         /// Constructor with a FileInfo object to an existing file.
39         /// </summary>
40         /// <param name="fileInfo"></param>
41         /// <param name="password"></param>
42         public static ZipArchive Open(FileInfo fileInfo, string password = null)
43         {
44             return Open(fileInfo, Options.None, password);
45         }
46
47         /// <summary>
48         /// Constructor expects a filepath to an existing file.
49         /// </summary>
50         /// <param name="filePath"></param>
51         /// <param name="options"></param>
52         /// <param name="password"></param>
53         public static ZipArchive Open(string filePath, Options options, string password = null)
54         {
55             filePath.CheckNotNullOrEmpty("filePath");
56             return Open(new FileInfo(filePath), options, password);
57         }
58
59         /// <summary>
60         /// Constructor with a FileInfo object to an existing file.
61         /// </summary>
62         /// <param name="fileInfo"></param>
63         /// <param name="options"></param>
64         /// <param name="password"></param>
65         public static ZipArchive Open(FileInfo fileInfo, Options options, string password = null)
66         {
67             fileInfo.CheckNotNull("fileInfo");
68             return new ZipArchive(fileInfo, options, password);
69         }
70 #endif
71
72         /// <summary>
73         /// Takes a seekable Stream as a source
74         /// </summary>
75         /// <param name="stream"></param>
76         /// <param name="password"></param>
77         public static ZipArchive Open(Stream stream, string password = null)
78         {
79             stream.CheckNotNull("stream");
80             return Open(stream, Options.None, password);
81         }
82
83         /// <summary>
84         /// Takes a seekable Stream as a source
85         /// </summary>
86         /// <param name="stream"></param>
87         /// <param name="options"></param>
88         /// <param name="password"></param>
89         public static ZipArchive Open(Stream stream, Options options, string password = null)
90         {
91             stream.CheckNotNull("stream");
92             return new ZipArchive(stream, options, password);
93         }
94
95 #if !PORTABLE && !NETFX_CORE
96         public static bool IsZipFile(string filePath, string password = null)
97         {
98             return IsZipFile(new FileInfo(filePath), password);
99         }
100
101         public static bool IsZipFile(FileInfo fileInfo, string password = null)
102         {
103             if (!fileInfo.Exists)
104             {
105                 return false;
106             }
107             using (Stream stream = fileInfo.OpenRead())
108             {
109                 return IsZipFile(stream, password);
110             }
111         }
112 #endif
113
114         public static bool IsZipFile(Stream stream, string password = null)
115         {
116             StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory(password);
117             try
118             {
119                 ZipHeader header =
120                     headerFactory.ReadStreamHeader(stream).FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
121                 if (header == null)
122                 {
123                     return false;
124                 }
125                 return Enum.IsDefined(typeof (ZipHeaderType), header.ZipHeaderType);
126             }
127             catch (CryptographicException)
128             {
129                 return true;
130             }
131             catch
132             {
133                 return false;
134             }
135         }
136
137 #if !PORTABLE && !NETFX_CORE
138         /// <summary>
139         /// Constructor with a FileInfo object to an existing file.
140         /// </summary>
141         /// <param name="fileInfo"></param>
142         /// <param name="options"></param>
143         /// <param name="password"></param>
144         internal ZipArchive(FileInfo fileInfo, Options options, string password = null)
145             : base(ArchiveType.Zip, fileInfo, options)
146         {
147             headerFactory = new SeekableZipHeaderFactory(password);
148         }
149
150         protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file, Options options)
151         {
152             if (FlagUtility.HasFlag(options, Options.KeepStreamsOpen))
153             {
154                 options = (Options)FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false);
155             }
156             return new ZipVolume(file.OpenRead(), options).AsEnumerable();
157         }
158 #endif
159
160         internal ZipArchive()
161             : base(ArchiveType.Zip)
162         {
163         }
164
165         /// <summary>
166         /// Takes multiple seekable Streams for a multi-part archive
167         /// </summary>
168         /// <param name="stream"></param>
169         /// <param name="options"></param>
170         /// <param name="password"></param>
171         internal ZipArchive(Stream stream, Options options, string password = null)
172             : base(ArchiveType.Zip, stream, options)
173         {
174             headerFactory = new SeekableZipHeaderFactory(password);
175         }
176
177         protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams, Options options)
178         {
179             return new ZipVolume(streams.First(), options).AsEnumerable();
180         }
181
182         protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
183         {
184             var volume = volumes.Single();
185             Stream stream = volume.Stream;
186             foreach (ZipHeader h in headerFactory.ReadSeekableHeader(stream))
187             {
188                 if (h != null)
189                 {
190                     switch (h.ZipHeaderType)
191                     {
192                         case ZipHeaderType.DirectoryEntry:
193                             {
194                                 yield return new ZipArchiveEntry(this,
195                                                                  new SeekableZipFilePart(headerFactory,
196                                                                                          h as DirectoryEntryHeader,
197                                                                                          stream));
198                             }
199                             break;
200                         case ZipHeaderType.DirectoryEnd:
201                             {
202                                 byte[] bytes = (h as DirectoryEndHeader).Comment;
203                                 volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length);
204                                 yield break;
205                             }
206                     }
207                 }
208             }
209         }     
210
211         protected override void SaveTo(Stream stream, CompressionInfo compressionInfo, Encoding encoding,
212                                        IEnumerable<ZipArchiveEntry> oldEntries,
213                                        IEnumerable<ZipArchiveEntry> newEntries)
214         {
215             using (var writer = new ZipWriter(stream, compressionInfo, string.Empty, encoding))
216             {
217                 foreach (var entry in oldEntries.Concat(newEntries)
218                                                 .Where(x => !x.IsDirectory))
219                 {
220                     using (var entryStream = entry.OpenEntryStream())
221                     {
222                         writer.Write(entry.Key, entryStream, entry.LastModifiedTime, string.Empty);
223                     }
224                 }
225             }
226         }
227
228         protected override ZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
229                                                        bool closeStream)
230         {
231             return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
232         }
233
234         public static ZipArchive Create()
235         {
236             return new ZipArchive();
237         }
238
239         protected override IReader CreateReaderForSolidExtraction()
240         {
241             var stream = Volumes.Single().Stream;
242             stream.Position = 0;
243             return ZipReader.Open(stream);
244         }
245     }
246 }