2b415eb5c8da2993e874371260e73d1cc4694858
[mono.git] / mcs / class / System.IO.Compression / ZipArchiveEntry.cs
1 //
2 // ZipArchiveEntry.cs
3 //
4 // Author:
5 //       Joao Matos <joao.matos@xamarin.com>
6 //       Martin Baulig <martin.baulig@xamarin.com>
7 //
8 // Copyright (c) 2013 Xamarin Inc. (http://www.xamarin.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27
28 using SharpCompress.Archive;
29
30 namespace System.IO.Compression
31 {
32         internal class ZipArchiveEntryStream : Stream, IDisposable
33         {
34                 private readonly ZipArchiveEntry entry;
35                 private Stream stream;
36
37                 public override bool CanRead {
38                         get { 
39                                 return stream.CanRead;
40                         }
41                 }
42
43                 public override bool CanSeek {
44                         get {
45                                 return entry.Archive.Mode != ZipArchiveMode.Read;
46                         }
47                 }
48
49                 public override bool CanWrite {
50                         get {
51                                 return entry.Archive.Mode != ZipArchiveMode.Read;
52                         }
53                 }
54
55                 public override long Length {
56                         get {
57                                 return stream.Length;
58                         }
59                 }
60
61                 public override long Position {
62                         get {
63                                 return stream.Position;
64                         }
65                         set {
66                                 stream.Position = value;
67                         }
68                 }
69
70                 public ZipArchiveEntryStream(ZipArchiveEntry entry, Stream stream)
71                 {
72                         this.entry = entry;
73                         this.stream = stream;
74                 }
75
76                 public override void Flush ()
77                 {
78                         stream.Flush();
79                 }
80
81                 public override long Seek (long offset, SeekOrigin origin)
82                 {
83                         return stream.Seek(offset, origin);
84                 }
85
86                 public override void SetLength (long value)
87                 {
88                         stream.SetLength(value);
89                 }
90
91                 public override int Read (byte[] buffer, int offset, int count)
92                 {
93                         return stream.Read(buffer, offset, count);
94                 }
95
96                 public override void Write (byte[] buffer, int offset, int count)
97                 {
98                         if (entry.Archive.Mode == ZipArchiveMode.Update && !entry.wasWritten)
99                         {
100                                 // Replace the read-only stream with a writeable memory stream.
101                                 SetWriteable();
102                                 entry.wasWritten = true;
103                         }
104
105                         stream.Write(buffer, offset, count);
106                 }
107
108                 internal void SetWriteable()
109                 {
110                         var archive = entry.Archive;
111
112                         var internalEntry = entry.entry;
113                         var newEntry = archive.CreateEntryInternal(internalEntry.Key);
114                         var newStream = newEntry.OpenEntryStream();
115
116                         var openStream = stream;
117                         var position = openStream.Position;
118
119                         entry.openStream = null;
120                         entry.Open();
121
122                         openStream.CopyTo(newStream);
123                         newStream.Position = position;
124                         openStream.Dispose();
125
126                         archive.zipFile.RemoveEntry(internalEntry);
127                         entry.entry = newEntry;
128                         stream = newStream;
129                 }
130
131                 public new void Dispose()
132                 {
133                         Dispose(true);
134                         GC.SuppressFinalize(this);
135                         base.Dispose();
136                 }
137
138                 protected override void Dispose(bool disposing)
139                 {
140                         if (disposing) 
141                         {
142                                 entry.openStream = null;
143                                 stream.Dispose();
144                         }
145                 }
146         }
147
148         public class ZipArchiveEntry
149         {
150                 internal SharpCompress.Archive.Zip.ZipArchiveEntry entry;
151                 internal ZipArchiveEntryStream openStream;
152         internal bool wasWritten;
153                 private bool wasDeleted;
154
155                 internal ZipArchiveEntry(ZipArchive     archive, SharpCompress.Archive.Zip.ZipArchiveEntry entry)
156                 {
157                         if (archive == null)
158                                 throw new ArgumentNullException("archive");
159
160                         if (entry == null)
161                                 throw new ArgumentNullException("entry");
162
163                         Archive = archive;
164                         this.entry = entry;
165                 }
166
167                 public ZipArchive Archive {
168                         get;
169                         private set;
170                 }
171
172                 public long CompressedLength {
173                         get {
174                                 if (Archive.Mode == ZipArchiveMode.Create)
175                                         throw new InvalidOperationException("Property cannot be retrieved when the mode is set to Create");
176
177                                 return entry.CompressedSize;
178                         }
179                 }
180
181                 public string FullName {
182                         get { return entry.Key; }
183                 }
184
185                 public DateTimeOffset LastWriteTime {
186                         get { return entry.LastModifiedTime.GetValueOrDefault(); }
187                         set { entry.LastModifiedTime = value.DateTime; }
188                 }
189
190                 public long Length {
191                         get {
192                                 if (Archive.Mode == ZipArchiveMode.Create)
193                                         throw new InvalidOperationException("Property cannot be retrieved when the mode is set to Create");
194
195                                 return entry.Size;
196                         }
197                 }
198
199                 public string Name {
200                         get { return Path.GetFileName(entry.Key); }
201                 }
202
203                 public void Delete()
204                 {
205                         if (Archive.disposed)
206                                 throw new ObjectDisposedException("The zip archive for this entry has been disposed.");
207
208                         if (Archive.Mode != ZipArchiveMode.Update)
209                                 throw new NotSupportedException("The zip archive for this entry was opened in a mode other than Update.");
210
211                         if (openStream != null)
212                                 throw new IOException("The entry is already open for reading or writing.");
213
214                         wasDeleted = true;
215                         Archive.zipFile.RemoveEntry(entry);
216                 }
217
218                 public Stream Open()
219                 {
220                         if (Archive.disposed)
221                                 throw new ObjectDisposedException("The zip archive for this entry has been disposed.");
222
223                         if (openStream != null && Archive.Mode == ZipArchiveMode.Update)
224                                 throw new IOException("The entry is already currently open for writing.");
225
226                         if (wasDeleted)
227                                 throw new IOException("The entry has been deleted from the archive.");
228
229                         if (Archive.Mode == ZipArchiveMode.Create && openStream != null)
230                                 throw new IOException("The archive for this entry was opened with the Create mode, and this entry has already been written to.");
231
232                         var entryStream = entry.OpenEntryStream();
233                         openStream = new ZipArchiveEntryStream(this, entryStream);
234
235                         return openStream;
236                 }
237
238                 public override string ToString()
239                 {
240                         return FullName;
241                 }
242         }
243 }