2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / WindowsBase / System.IO.Packaging / ZipPackage.cs
blob0a113364c520aeccf46720eca559177b4c1e9b7c
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
22 // Authors:
23 // Chris Toshok (toshok@ximian.com)
26 using System;
27 using System.Collections.Generic;
28 using System.IO;
29 using System.Xml;
30 using zipsharp;
32 namespace System.IO.Packaging {
34 class UriComparer : IEqualityComparer<Uri>
36 public int GetHashCode(Uri uri)
38 return 1;
41 public bool Equals(Uri x, Uri y)
43 return x.OriginalString.Equals (y.OriginalString, StringComparison.OrdinalIgnoreCase);
47 public sealed class ZipPackage : Package
49 private const string ContentNamespace = "http://schemas.openxmlformats.org/package/2006/content-types";
50 private const string ContentUri = "[Content_Types].xml";
52 Dictionary<Uri, ZipPackagePart> parts;
53 internal Dictionary<Uri, MemoryStream> PartStreams = new Dictionary<Uri, MemoryStream> (new UriComparer());
55 internal Stream PackageStream { get; set; }
57 Dictionary<Uri, ZipPackagePart> Parts {
58 get {
59 if (parts == null)
60 LoadParts ();
61 return parts;
65 internal ZipPackage (FileAccess access, Stream stream)
66 : base (access)
68 PackageStream = stream;
71 internal ZipPackage (FileAccess access, Stream stream, bool streaming)
72 : base (access, streaming)
74 PackageStream = stream;
77 protected override void Dispose (bool disposing)
79 foreach (Stream s in PartStreams.Values)
80 s.Close ();
82 PackageStream.Close ();
85 protected override void FlushCore ()
87 // Ensure that all the data has been read out of the package
88 // stream already. Otherwise we'll lose data when we recreate the zip
89 foreach (ZipPackagePart part in Parts.Values)
90 part.GetStream ().Dispose ();
92 // Empty the package stream
93 PackageStream.Position = 0;
94 PackageStream.SetLength (0);
96 // Recreate the zip file
97 using (ZipArchive archive = new ZipArchive(PackageStream, Append.Create, false)) {
99 // Write all the part streams
100 foreach (ZipPackagePart part in Parts.Values) {
101 Stream partStream = part.GetStream ();
102 partStream.Seek (0, SeekOrigin.Begin);
104 using (Stream destination = archive.GetStream (part.Uri.ToString ().Substring(1), part.CompressionOption)) {
105 int count = (int) Math.Min (2048, partStream.Length);
106 byte[] buffer = new byte [count];
108 while ((count = partStream.Read (buffer, 0, buffer.Length)) != 0)
109 destination.Write (buffer, 0, count);
113 using (Stream s = archive.GetStream (ContentUri, CompressionOption.Maximum))
114 WriteContentType (s);
118 protected override PackagePart CreatePartCore (Uri partUri, string contentType, CompressionOption compressionOption)
120 ZipPackagePart part = new ZipPackagePart (this, partUri, contentType, compressionOption);
121 Parts.Add (part.Uri, part);
122 return part;
125 protected override void DeletePartCore (Uri partUri)
127 Parts.Remove (partUri);
130 protected override PackagePart GetPartCore (Uri partUri)
132 ZipPackagePart part;
133 Parts.TryGetValue (partUri, out part);
134 return part;
137 protected override PackagePart[] GetPartsCore ()
139 ZipPackagePart[] p = new ZipPackagePart [Parts.Count];
140 Parts.Values.CopyTo (p, 0);
141 return p;
144 void LoadParts ()
146 parts = new Dictionary<Uri, ZipPackagePart> (new UriComparer());
147 try {
148 using (UnzipArchive archive = new UnzipArchive (PackageStream)) {
150 // Load the content type map file
151 XmlDocument doc = new XmlDocument ();
152 using (Stream s = archive.GetStream (ContentUri))
153 doc.Load (s);
155 XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
156 manager.AddNamespace ("content", ContentNamespace);
158 // The file names in the zip archive are not prepended with '/'
159 foreach (string file in archive.GetFiles ()) {
160 XmlNode node;
161 CompressionOption compression = archive.GetCompressionLevel (file);
163 if (file == RelationshipUri.ToString ().Substring (1))
165 CreatePartCore (RelationshipUri, RelationshipContentType, compression);
166 continue;
169 string xPath = string.Format ("/content:Types/content:Override[@PartName='/{0}']", file);
170 node = doc.SelectSingleNode (xPath, manager);
172 if (node == null)
174 string ext = Path.GetExtension (file);
175 if (ext.StartsWith("."))
176 ext = ext.Substring (1);
177 xPath = string.Format("/content:Types/content:Default[@Extension='{0}']", ext);
178 node = doc.SelectSingleNode (xPath, manager);
181 // What do i do if the node is null? This means some has tampered with the
182 // package file manually
183 if (node != null)
184 CreatePartCore (new Uri ("/" + file, UriKind.Relative), node.Attributes["ContentType"].Value, compression);
187 } catch {
188 // The archive is invalid - therefore no parts
192 void WriteContentType (Stream s)
194 XmlDocument doc = new XmlDocument ();
195 XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
196 manager.AddNamespace ("content", ContentNamespace);
198 doc.AppendChild(doc.CreateNode (XmlNodeType.XmlDeclaration, "", ""));
200 XmlNode root = doc.CreateNode (XmlNodeType.Element, "Types", ContentNamespace);
201 doc.AppendChild (root);
202 foreach (ZipPackagePart part in Parts.Values)
204 XmlNode node = doc.CreateNode (XmlNodeType.Element, "Override", ContentNamespace);
206 XmlAttribute contentType = doc.CreateAttribute ("ContentType");
207 contentType.Value = part.ContentType;
209 XmlAttribute name = doc.CreateAttribute ("PartName");
210 name.Value = part.Uri.ToString ();
213 node.Attributes.Append (contentType);
214 node.Attributes.Append (name);
216 root.AppendChild (node);
219 using (XmlTextWriter writer = new XmlTextWriter (s, System.Text.Encoding.UTF8))
220 doc.WriteTo (writer);