Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / FastZip.cs @ 0eea575a

History | View | Annotate | Download (23.9 kB)

1
// FastZip.cs
2
//
3
// Copyright 2005 John Reilly
4
//
5
// This program is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU General Public License
7
// as published by the Free Software Foundation; either version 2
8
// of the License, or (at your option) any later version.
9
//
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
//
19
// Linking this library statically or dynamically with other modules is
20
// making a combined work based on this library.  Thus, the terms and
21
// conditions of the GNU General Public License cover the whole
22
// combination.
23
// 
24
// As a special exception, the copyright holders of this library give you
25
// permission to link this library with independent modules to produce an
26
// executable, regardless of the license terms of these independent
27
// modules, and to copy and distribute the resulting executable under
28
// terms of your choice, provided that you also meet, for each linked
29
// independent module, the terms and conditions of the license of that
30
// module.  An independent module is a module which is not derived from
31
// or based on this library.  If you modify this library, you may extend
32
// this exception to your version of the library, but you are not
33
// obligated to do so.  If you do not wish to do so, delete this
34
// exception statement from your version.
35

    
36
using System;
37
using System.IO;
38
using ICSharpCode.SharpZipLib.Silverlight.Core;
39
using ICSharpCode.SharpZipLib.Silverlight.Zip;
40
using ICSharpCode.SharpZipLib.Zip;
41

    
42
namespace ICSharpCode.SharpZipLib.Silverlight.Zip
43
{
44
    /// <summary>
45
    /// FastZipEvents supports all events applicable to <see cref="FastZip">FastZip</see> operations.
46
    /// </summary>
47
    public class FastZipEvents
48
    {
49
        /// <summary>
50
        /// Delegate to invoke when processing directories.
51
        /// </summary>
52
        public ProcessDirectoryHandler ProcessDirectory;
53
		
54
        /// <summary>
55
        /// Delegate to invoke when processing files.
56
        /// </summary>
57
        public ProcessFileHandler ProcessFile;
58

    
59
        /// <summary>
60
        /// Delegate to invoke during processing of files.
61
        /// </summary>
62
        public ProgressHandler Progress;
63

    
64
        /// <summary>
65
        /// Delegate to invoke when processing for a file has been completed.
66
        /// </summary>
67
        public CompletedFileHandler CompletedFile;
68
		
69
        /// <summary>
70
        /// Delegate to invoke when processing directory failures.
71
        /// </summary>
72
        public DirectoryFailureHandler DirectoryFailure;
73
		
74
        /// <summary>
75
        /// Delegate to invoke when processing file failures.
76
        /// </summary>
77
        public FileFailureHandler FileFailure;
78
		
79
        /// <summary>
80
        /// Raise the <see cref="DirectoryFailure">directory failure</see> event.
81
        /// </summary>
82
        /// <param name="directory">The directory causing the failure.</param>
83
        /// <param name="e">The exception for this event.</param>
84
        /// <returns>A boolean indicating if execution should continue or not.</returns>
85
        public bool OnDirectoryFailure(string directory, Exception e)
86
        {
87
            bool result = false;
88
            if ( DirectoryFailure != null ) {
89
                var args = new ScanFailureEventArgs(directory, e);
90
                DirectoryFailure(this, args);
91
                result = args.ContinueRunning;
92
            }
93
            return result;
94
        }
95
		
96
        /// <summary>
97
        /// Raises the <see cref="FileFailure">file failure delegate</see>.
98
        /// </summary>
99
        /// <param name="file">The file causing the failure.</param>
100
        /// <param name="e">The exception for this failure.</param>
101
        /// <returns>A boolean indicating if execution should continue or not.</returns>
102
        public bool OnFileFailure(string file, Exception e)
103
        {
104
            bool result = false;
105
            if ( FileFailure != null ) {
106
                var args = new ScanFailureEventArgs(file, e);
107
                FileFailure(this, args);
108
                result = args.ContinueRunning;
109
            }
110
            return result;
111
        }
112
		
113
        /// <summary>
114
        /// Fires the <see cref="ProcessFile">Process File delegate</see>.
115
        /// </summary>
116
        /// <param name="file">The file being processed.</param>
117
        /// <returns>A boolean indicating if execution should continue or not.</returns>
118
        public bool OnProcessFile(string file)
119
        {
120
            bool result = true;
121
            if ( ProcessFile != null ) {
122
                var args = new ScanEventArgs(file);
123
                ProcessFile(this, args);
124
                result = args.ContinueRunning;
125
            }
126
            return result;
127
        }
128

    
129
        /// <summary>
130
        /// Fires the CompletedFile delegate
131
        /// </summary>
132
        /// <param name="file">The file whose processing has been completed.</param>
133
        /// <returns>A boolean indicating if execution should continue or not.</returns>
134
        public bool OnCompletedFile(string file)
135
        {
136
            var result = true;
137
            if ( CompletedFile != null ) {
138
                var args = new ScanEventArgs(file);
139
                CompletedFile(this, args);
140
                result = args.ContinueRunning;
141
            }
142
            return result;
143
        }
144
		
145
        /// <summary>
146
        /// Fires the <see cref="ProcessDirectory">process directory</see> delegate.
147
        /// </summary>
148
        /// <param name="directory">The directory being processed.</param>
149
        /// <param name="hasMatchingFiles">Flag indicating if the directory has matching files as determined by the current filter.</param>
150
        /// <returns>A <see cref="bool"/> of true if the operation should continue; false otherwise.</returns>
151
        public bool OnProcessDirectory(string directory, bool hasMatchingFiles)
152
        {
153
            bool result = true;
154
            if ( ProcessDirectory != null ) {
155
                var args = new DirectoryEventArgs(directory, hasMatchingFiles);
156
                ProcessDirectory(this, args);
157
                result = args.ContinueRunning;
158
            }
159
            return result;
160
        }
161

    
162
        /// <summary>
163
        /// The minimum timespan between <see cref="Progress"/> events.
164
        /// </summary>
165
        /// <value>The minimum period of time between <see cref="Progress"/> events.</value>
166
        /// <seealso cref="Progress"/>
167
        public TimeSpan ProgressInterval
168
        {
169
            get { return progressInterval_; }
170
            set { progressInterval_ = value; }
171
        }
172

    
173
        #region Instance Fields
174
        TimeSpan progressInterval_ = TimeSpan.FromSeconds(3);
175
        #endregion
176
    }
177

    
178
    /// <summary>
179
    /// FastZip provides facilities for creating and extracting zip files.
180
    /// </summary>
181
    public class FastZip
182
    {
183
        #region Enumerations
184
        /// <summary>
185
        /// Defines the desired handling when overwriting files during extraction.
186
        /// </summary>
187
        public enum Overwrite 
188
        {
189
            /// <summary>
190
            /// Prompt the user to confirm overwriting
191
            /// </summary>
192
            Prompt,
193
            /// <summary>
194
            /// Never overwrite files.
195
            /// </summary>
196
            Never,
197
            /// <summary>
198
            /// Always overwrite files.
199
            /// </summary>
200
            Always
201
        }
202
        #endregion
203
		
204
        #region Constructors
205
        /// <summary>
206
        /// Initialise a default instance of <see cref="FastZip"/>.
207
        /// </summary>
208
        public FastZip()
209
        {
210
        }
211

    
212
        /// <summary>
213
        /// Initialise a new instance of <see cref="FastZip"/>
214
        /// </summary>
215
        /// <param name="events">The <see cref="FastZipEvents">events</see> to use during operations.</param>
216
        public FastZip(FastZipEvents events)
217
        {
218
            events_ = events;
219
        }
220
        #endregion
221
		
222
        #region Properties
223

    
224
        /// <summary>
225
        /// Get/set a value indicating wether empty directories should be created.
226
        /// </summary>
227
        public bool CreateEmptyDirectories { get; set; }
228

    
229
        /// <summary>
230
        /// Get / set the password value.
231
        /// </summary>
232
        public string Password
233
        {
234
            get { return password_; }
235
            set { password_ = value; }
236
        }
237

    
238
        /// <summary>
239
        /// Get or set the <see cref="INameTransform"></see> active when creating Zip files.
240
        /// </summary>
241
        /// <seealso cref="EntryFactory"></seealso>
242
        public INameTransform NameTransform
243
        {
244
            get { return entryFactory_.NameTransform; }
245
            set {
246
                entryFactory_.NameTransform = value;
247
            }
248
        }
249

    
250
        /// <summary>
251
        /// Get or set the <see cref="IEntryFactory"></see> active when creating Zip files.
252
        /// </summary>
253
        public IEntryFactory EntryFactory
254
        {
255
            get { return entryFactory_; }
256
            set {
257
                entryFactory_ = value ?? new ZipEntryFactory();
258
            }
259
        }
260
		
261
        /// <summary>
262
        /// Get/set a value indicating wether file dates and times should 
263
        /// be restored when extracting files from an archive.
264
        /// </summary>
265
        /// <remarks>The default value is false.</remarks>
266
        public bool RestoreDateTimeOnExtract
267
        {
268
            get {
269
                return restoreDateTimeOnExtract_;
270
            }
271
            set {
272
                restoreDateTimeOnExtract_ = value;
273
            }
274
        }
275

    
276
        /// <summary>
277
        /// Get/set a value indicating wether file attributes should
278
        /// be restored during extract operations
279
        /// </summary>
280
        public bool RestoreAttributesOnExtract { get; set; }
281

    
282
        #endregion
283
		
284
        #region Delegates
285
        /// <summary>
286
        /// Delegate called when confirming overwriting of files.
287
        /// </summary>
288
        public delegate bool ConfirmOverwriteDelegate(string fileName);
289
        #endregion
290
		
291
        #region CreateZip
292
        /// <summary>
293
        /// Create a zip file.
294
        /// </summary>
295
        /// <param name="zipFileName">The name of the zip file to create.</param>
296
        /// <param name="sourceDirectory">The directory to source files from.</param>
297
        /// <param name="recurse">True to recurse directories, false for no recursion.</param>
298
        /// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
299
        /// <param name="directoryFilter">The <see cref="PathFilter">directory filter</see> to apply.</param>
300
        public void CreateZip(string zipFileName, string sourceDirectory, 
301
                              bool recurse, string fileFilter, string directoryFilter)
302
        {
303
            CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter);
304
        }
305
		
306
        /// <summary>
307
        /// Create a zip file/archive.
308
        /// </summary>
309
        /// <param name="zipFileName">The name of the zip file to create.</param>
310
        /// <param name="sourceDirectory">The directory to obtain files and directories from.</param>
311
        /// <param name="recurse">True to recurse directories, false for no recursion.</param>
312
        /// <param name="fileFilter">The file filter to apply.</param>
313
        public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter)
314
        {
315
            CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, null);
316
        }
317

    
318
        /// <summary>
319
        /// Create a zip archive sending output to the <paramref name="outputStream"/> passed.
320
        /// </summary>
321
        /// <param name="outputStream">The stream to write archive data to.</param>
322
        /// <param name="sourceDirectory">The directory to source files from.</param>
323
        /// <param name="recurse">True to recurse directories, false for no recursion.</param>
324
        /// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
325
        /// <param name="directoryFilter">The <see cref="PathFilter">directory filter</see> to apply.</param>
326
        public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter)
327
        {
328
            NameTransform = new ZipNameTransform(sourceDirectory);
329
            sourceDirectory_ = sourceDirectory;
330

    
331
            using ( outputStream_ = new ZipOutputStream(outputStream) ) {
332

    
333
                if (password_ != null)
334
                {
335
                    outputStream_.Password = password_;
336
                }
337

    
338
                var scanner = new FileSystemScanner(fileFilter, directoryFilter);
339
                scanner.ProcessFile += ProcessFile;
340
                if ( CreateEmptyDirectories ) {
341
                    scanner.ProcessDirectory += ProcessDirectory;
342
                }
343
				
344
                if (events_ != null) {
345
                    if ( events_.FileFailure != null ) {
346
                        scanner.FileFailure += events_.FileFailure;
347
                    }
348

    
349
                    if ( events_.DirectoryFailure != null ) {
350
                        scanner.DirectoryFailure += events_.DirectoryFailure;
351
                    }
352
                }
353

    
354
                scanner.Scan(sourceDirectory, recurse);
355
            }
356
        }
357

    
358
        #endregion
359
		
360
        #region ExtractZip
361
        /// <summary>
362
        /// Extract the contents of a zip file.
363
        /// </summary>
364
        /// <param name="zipFileName">The zip file to extract from.</param>
365
        /// <param name="targetDirectory">The directory to save extracted information in.</param>
366
        /// <param name="fileFilter">A filter to apply to files.</param>
367
        public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter) 
368
        {
369
            ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null, restoreDateTimeOnExtract_);
370
        }
371
		
372
        /// <summary>
373
        /// Extract the contents of a zip file.
374
        /// </summary>
375
        /// <param name="zipFileName">The zip file to extract from.</param>
376
        /// <param name="targetDirectory">The directory to save extracted information in.</param>
377
        /// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>
378
        /// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>
379
        /// <param name="fileFilter">A filter to apply to files.</param>
380
        /// <param name="directoryFilter">A filter to apply to directories.</param>
381
        /// <param name="restoreDateTime">Flag indicating wether to restore the date and time for extracted files.</param>
382
        public void ExtractZip(string zipFileName, string targetDirectory, 
383
                               Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, 
384
                               string fileFilter, string directoryFilter, bool restoreDateTime)
385
        {
386
            if ( (overwrite == Overwrite.Prompt) && (confirmDelegate == null) ) {
387
                throw new ArgumentNullException("confirmDelegate");
388
            }
389

    
390
            continueRunning_ = true;
391
            overwrite_ = overwrite;
392
            confirmDelegate_ = confirmDelegate;
393
            targetDirectory_ = targetDirectory;
394
            fileFilter_ = new NameFilter(fileFilter);
395
            directoryFilter_ = new NameFilter(directoryFilter);
396
            restoreDateTimeOnExtract_ = restoreDateTime;
397
			
398
            using ( zipFile_ = new ZipFile(zipFileName) ) {
399

    
400
#if !NETCF_1_0
401
                if (password_ != null) {
402
                    zipFile_.Password = password_;
403
                }
404
#endif
405

    
406
                System.Collections.IEnumerator enumerator = zipFile_.GetEnumerator();
407
                while ( continueRunning_ && enumerator.MoveNext()) {
408
                    var entry = (ZipEntry) enumerator.Current;
409
                    if ( entry.IsFile )
410
                    {
411
                        if ( directoryFilter_.IsMatch(Path.GetDirectoryName(entry.Name)) && fileFilter_.IsMatch(entry.Name) ) {
412
                            ExtractEntry(entry);
413
                        }
414
                    }
415
                    else if ( entry.IsDirectory ) {
416
                        if ( directoryFilter_.IsMatch(entry.Name) && CreateEmptyDirectories ) {
417
                            ExtractEntry(entry);
418
                        }
419
                    }
420
                }
421
            }
422
        }
423
        #endregion
424
		
425
        #region Internal Processing
426
        void ProcessDirectory(object sender, DirectoryEventArgs e)
427
        {
428
            if ( !e.HasMatchingFiles && CreateEmptyDirectories ) {
429
                if ( events_ != null ) {
430
                    events_.OnProcessDirectory(e.Name, e.HasMatchingFiles);
431
                }
432
				
433
                if ( e.ContinueRunning ) {
434
                    if (e.Name != sourceDirectory_) {
435
                        ZipEntry entry = entryFactory_.MakeDirectoryEntry(e.Name);
436
                        outputStream_.PutNextEntry(entry);
437
                    }
438
                }
439
            }
440
        }
441
		
442
        void ProcessFile(object sender, ScanEventArgs e)
443
        {
444
            if ( (events_ != null) && (events_.ProcessFile != null) ) {
445
                events_.ProcessFile(sender, e);
446
            }
447
			
448
            if ( e.ContinueRunning ) {
449
                ZipEntry entry = entryFactory_.MakeFileEntry(e.Name);
450
                outputStream_.PutNextEntry(entry);
451
                AddFileContents(e.Name);
452
            }
453
        }
454

    
455
        void AddFileContents(string name)
456
        {
457
            if ( buffer_ == null ) {
458
                buffer_ = new byte[4096];
459
            }
460

    
461
            using (FileStream stream = File.OpenRead(name)) {
462
                if ((events_ != null) && (events_.Progress != null)) {
463
                    StreamUtils.Copy(stream, outputStream_, buffer_,
464
                                     events_.Progress, events_.ProgressInterval, this, name);
465
                }
466
                else {
467
                    StreamUtils.Copy(stream, outputStream_, buffer_);
468
                }
469
            }
470

    
471
            if (events_ != null) {
472
                continueRunning_ = events_.OnCompletedFile(name);
473
            }
474
        }
475
		
476
        void ExtractFileEntry(ZipEntry entry, string targetName)
477
        {
478
            bool proceed = true;
479
            if ( overwrite_ != Overwrite.Always ) {
480
                if ( File.Exists(targetName) ) {
481
                    if ( (overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null) ) {
482
                        proceed = confirmDelegate_(targetName);
483
                    }
484
                    else {
485
                        proceed = false;
486
                    }
487
                }
488
            }
489
			
490
            if ( proceed ) {
491
                if ( events_ != null ) {
492
                    continueRunning_ = events_.OnProcessFile(entry.Name);
493
                }
494
			
495
                if ( continueRunning_ ) {
496
                    try {
497
                        using ( FileStream outputStream = File.Create(targetName) ) {
498
                            if ( buffer_ == null ) {
499
                                buffer_ = new byte[4096];
500
                            }
501
                            if ((events_ != null) && (events_.Progress != null))
502
                            {
503
                                StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_,
504
                                                 events_.Progress, events_.ProgressInterval, this, entry.Name);
505
                            }
506
                            else
507
                            {
508
                                StreamUtils.Copy(zipFile_.GetInputStream(entry), outputStream, buffer_);
509
                            }
510
							
511
                            if (events_ != null) {
512
                                continueRunning_ = events_.OnCompletedFile(entry.Name);
513
                            }
514
                        }
515

    
516
                        //if ( restoreDateTimeOnExtract_ ) {
517
                        //File.SetLastWriteTime(targetName, entry.DateTime);
518
                        //}
519
						
520
                        if ( RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1)) {
521
                            var fileAttributes = (FileAttributes) entry.ExternalFileAttributes;
522
                            // TODO: FastZip - Setting of other file attributes on extraction is a little trickier.
523
                            fileAttributes &= (FileAttributes.Archive | FileAttributes.Normal | FileAttributes.ReadOnly | FileAttributes.Hidden);
524
                            File.SetAttributes(targetName, fileAttributes);
525
                        }
526
                    }
527
                    catch(Exception ex) {
528
                        continueRunning_ = events_ != null && events_.OnFileFailure(targetName, ex);
529
                    }
530
                }
531
            }
532
        }
533

    
534
        void ExtractEntry(ZipEntry entry)
535
        {
536
            bool doExtraction = false;
537
			
538
            string nameText = entry.Name;
539
			
540
            if ( entry.IsFile ) {
541
                // TODO: Translate invalid names allowing extraction still.
542
                doExtraction = NameIsValid(nameText) && entry.IsCompressionMethodSupported();
543
            }
544
            else if ( entry.IsDirectory ) {
545
                doExtraction = NameIsValid(nameText);
546
            }
547
			
548
            // TODO: Fire delegate were compression method not supported, or name is invalid?
549

    
550
            string dirName = null;
551
            string targetName = null;
552
			
553
            if ( doExtraction ) {
554
                // Handle invalid entry names by chopping of path root.
555
                if (Path.IsPathRooted(nameText)) {
556
                    string workName = Path.GetPathRoot(nameText);
557
                    nameText = nameText.Substring(workName.Length);
558
                }
559
				
560
                if ( nameText.Length > 0 )
561
                {
562
                    targetName = Path.Combine(targetDirectory_, nameText);
563
                    dirName = entry.IsDirectory ? targetName : Path.GetDirectoryName(Path.GetFullPath(targetName));
564
                }
565
                else {
566
                    doExtraction = false;
567
                }
568
            }
569
			
570
            if ( doExtraction && !Directory.Exists(dirName) ) {
571
                if ( !entry.IsDirectory || CreateEmptyDirectories ) {
572
                    try {
573
                        Directory.CreateDirectory(dirName);
574
                    }
575
                    catch (Exception ex) {
576
                        doExtraction = false;
577
                        if ( events_ != null ) {
578
                            continueRunning_ = entry.IsDirectory ? events_.OnDirectoryFailure(targetName, ex) : events_.OnFileFailure(targetName, ex);
579
                        }
580
                        else {
581
                            continueRunning_ = false;
582
                        }
583
                    }
584
                }
585
            }
586
			
587
            if ( doExtraction && entry.IsFile ) {
588
                ExtractFileEntry(entry, targetName);
589
            }
590
        }
591

    
592
        static int MakeExternalAttributes(FileSystemInfo info)
593
        {
594
            return (int)info.Attributes;
595
        }
596

    
597
        static bool NameIsValid(string name)
598
        {
599
            return !string.IsNullOrEmpty(name) &&
600
                   (name.IndexOfAny(Path.GetInvalidPathChars()) < 0);
601
        }
602
        #endregion
603
		
604
        #region Instance Fields
605
        bool continueRunning_;
606
        byte[] buffer_;
607
        ZipOutputStream outputStream_;
608
        ZipFile zipFile_;
609
        string targetDirectory_;
610
        string sourceDirectory_;
611
        NameFilter fileFilter_;
612
        NameFilter directoryFilter_;
613
        Overwrite overwrite_;
614
        ConfirmOverwriteDelegate confirmDelegate_;
615
		
616
        bool restoreDateTimeOnExtract_;
617
        readonly FastZipEvents events_;
618
        IEntryFactory entryFactory_ = new ZipEntryFactory();
619
		
620
        string password_;
621

    
622
        #endregion
623
    }
624
}