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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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)
23 // Chris Toshok (toshok@ximian.com)
27 using System.Collections.Generic;
32 namespace System.IO.Packaging {
34 class UriComparer : IEqualityComparer<Uri>
36 public int GetHashCode(Uri uri)
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 {
65 internal ZipPackage (FileAccess access, Stream stream)
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)
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))) {
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))
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);
125 protected override void DeletePartCore (Uri partUri)
127 Parts.Remove (partUri);
130 protected override PackagePart GetPartCore (Uri partUri)
133 Parts.TryGetValue (partUri, out part);
137 protected override PackagePart[] GetPartsCore ()
139 ZipPackagePart[] p = new ZipPackagePart [Parts.Count];
140 Parts.Values.CopyTo (p, 0);
146 parts = new Dictionary<Uri, ZipPackagePart> (new UriComparer());
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))
155 XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
156 manager.AddNamespace ("content", ContentNamespace);
158 foreach (string file in archive.GetFiles ()) {
161 if (file == RelationshipUri.ToString ().Substring (1))
163 // FIXME: I shouldn't use CompressionOption.NotCompressed - i should read it from the zip archive
164 CreatePartCore (RelationshipUri, RelationshipContentType, CompressionOption.NotCompressed);
168 string xPath = string.Format ("/content:Types/content:Override[@PartName='{0}']", file);
169 node = doc.SelectSingleNode (xPath, manager);
173 string ext = Path.GetExtension (file);
174 if (ext.StartsWith("."))
175 ext = ext.Substring (1);
176 xPath = string.Format("/content:Types/content:Default[@Extension='{0}']", ext);
177 node = doc.SelectSingleNode (xPath, manager);
180 // What do i do if the node is null? This means some has tampered with the
181 // package file manually
183 // FIXME: I shouldn't use CompressionOption.NotCompressed - i should read it from the zip archive
185 CreatePartCore (new Uri ("/" + file, UriKind.Relative), node.Attributes["ContentType"].Value, CompressionOption.NotCompressed);
189 // The archive is invalid - therefore no parts
193 void WriteContentType (Stream s)
195 XmlDocument doc = new XmlDocument ();
196 XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
197 manager.AddNamespace ("content", ContentNamespace);
199 doc.AppendChild(doc.CreateNode (XmlNodeType.XmlDeclaration, "", ""));
201 XmlNode root = doc.CreateNode (XmlNodeType.Element, "Types", ContentNamespace);
202 doc.AppendChild (root);
203 foreach (ZipPackagePart part in Parts.Values)
205 XmlNode node = doc.CreateNode (XmlNodeType.Element, "Override", ContentNamespace);
207 XmlAttribute contentType = doc.CreateAttribute ("ContentType");
208 contentType.Value = part.ContentType;
210 XmlAttribute name = doc.CreateAttribute ("PartName");
211 name.Value = part.Uri.ToString ().Substring(1);
214 node.Attributes.Append (contentType);
215 node.Attributes.Append (name);
217 root.AppendChild (node);
220 using (XmlTextWriter writer = new XmlTextWriter (s, System.Text.Encoding.UTF8))
221 doc.WriteTo (writer);