2006-12-16 Gabor Kelemen <kelemeng@gnome.hu>
[beagle.git] / Filters / FilterArchive.cs
blobff4eccedaeccb7f996784a01e42e8d26720882d3
1 //
2 // FilterArchive.cs
3 //
4 // Copyright (C) 2004-2006 Novell, Inc.
5 //
6 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.IO;
31 using ICSharpCode.SharpZipLib;
32 using ICSharpCode.SharpZipLib.Zip;
33 using ICSharpCode.SharpZipLib.GZip;
34 using ICSharpCode.SharpZipLib.BZip2;
35 using ICSharpCode.SharpZipLib.Tar;
37 using Beagle;
38 using Beagle.Daemon;
39 using Beagle.Util;
41 namespace Beagle.Filters {
43 public class FilterArchive : Beagle.Daemon.Filter {
45 internal delegate ArchiveEntry GetNextEntry ();
47 private FileInfo file_info;
48 private Stream archive_stream;
49 private GetNextEntry get_next_entry;
51 // Fairly arbitrary number of files to limit
52 private const int MAX_CHILDREN = 30;
54 public FilterArchive ()
56 // 1: Store entry names as text content
57 SetVersion (1);
59 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/zip"));
60 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-bzip-compressed-tar"));
61 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-compressed-tar"));
62 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-tar"));
63 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-tgz"));
64 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-gzip"));
65 AddSupportedFlavor (FilterFlavor.NewFromMimeType ("application/x-bzip"));
68 protected override void DoOpen (FileInfo file_info)
70 this.file_info = file_info;
72 switch (MimeType) {
73 case "application/zip":
74 archive_stream = new ZipInputStream (Stream);
75 get_next_entry = GetNextEntryZip;
76 break;
78 case "application/x-bzip-compressed-tar":
79 archive_stream = new TarInputStream (new BZip2InputStream (Stream));
80 get_next_entry = GetNextEntryTar;
81 break;
83 case "application/x-compressed-tar":
84 case "application/x-tgz":
85 archive_stream = new TarInputStream (new GZipInputStream (Stream));
86 get_next_entry = GetNextEntryTar;
87 break;
89 case "application/x-tar":
90 archive_stream = new TarInputStream (Stream);
91 get_next_entry = GetNextEntryTar;
92 break;
94 case "application/x-gzip":
95 archive_stream = new GZipInputStream (Stream);
96 get_next_entry = GetNextEntrySingle;
97 break;
99 case "application/x-bzip":
100 archive_stream = new BZip2InputStream (Stream);
101 get_next_entry = GetNextEntrySingle;
102 break;
104 default:
105 throw new ArgumentException ("Invalid or unsupported mime type.");
110 protected override void DoPullProperties ()
112 // FIXME: Fetch the archive properties.
115 protected override void DoPullSetup ()
117 ArchiveEntry a_entry;
118 int count = 0;
120 while ((a_entry = this.get_next_entry ()) != null && count < MAX_CHILDREN) {
121 // FIXME: For nested archives, create uid:foo#bar
122 // instead of uid:foo#xxx#bar (avoid duplicates ?)
123 Indexable child = new Indexable (new Uri (Uri.ToString () + "#" + a_entry.Name, true));
125 child.CacheContent = true;
126 child.MimeType = a_entry.MimeType;
128 child.DisplayUri = new Uri (DisplayUri.ToString () + "#" + a_entry.Name, true);
129 child.ContentUri = UriFu.PathToFileUri (a_entry.TempFile);
130 child.DeleteContent = true;
132 child.AddProperty (Property.NewBool ("fixme:inside_archive", true));
134 child.AddProperty (Property.NewKeyword ("fixme:relativeuri", a_entry.Name));
135 child.AddProperty (Property.New ("fixme:comment", a_entry.Comment));
137 foreach (Property prop in Property.StandardFileProperties (Path.GetFileName (a_entry.Name), false))
138 child.AddProperty (prop);
140 AddChildIndexable (child);
142 // Store file names in the archive
143 AppendText (Path.GetFileName (a_entry.Name));
144 AppendWhiteSpace ();
146 ++count;
150 internal class ArchiveEntry {
151 public string Name;
152 public string MimeType;
153 public DateTime Modified;
154 public string Comment;
156 public string TempFile;
159 private static string StoreStreamInTempFile (Stream stream, DateTime mtime)
161 if (stream == null)
162 return null;
164 string filename = Path.GetTempFileName ();
165 FileStream file_stream = File.OpenWrite (filename);
167 Mono.Unix.Native.Syscall.chmod (filename, (Mono.Unix.Native.FilePermissions) 384); // 384 is 0600
169 BufferedStream buffered_stream = new BufferedStream (file_stream);
171 byte [] buffer = new byte [8192];
172 int read;
174 do {
175 read = stream.Read (buffer, 0, buffer.Length);
176 if (read > 0)
177 buffered_stream.Write (buffer, 0, read);
178 } while (read > 0);
180 buffered_stream.Close ();
182 File.SetLastWriteTimeUtc (filename, mtime);
184 Mono.Unix.Native.Syscall.chmod (filename, (Mono.Unix.Native.FilePermissions) 256); // 256 is 0400
186 return filename;
189 private ArchiveEntry GetNextEntryZip ()
191 ZipInputStream zip_stream = (ZipInputStream) archive_stream;
192 ZipEntry zip_entry;
194 do {
195 zip_entry = zip_stream.GetNextEntry ();
196 } while (zip_entry != null && zip_entry.IsDirectory);
198 // End of the entries.
199 if (zip_entry == null)
200 return null;
202 ArchiveEntry entry = new ArchiveEntry ();
203 entry.Name = zip_entry.Name;
204 entry.Modified = zip_entry.DateTime; // FIXME: Not sure zip_entry.DateTime is UTC.
205 entry.Comment = zip_entry.Comment;
207 entry.TempFile = StoreStreamInTempFile (archive_stream, entry.Modified);
208 entry.MimeType = XdgMime.GetMimeType (entry.TempFile);
210 return entry;
213 private ArchiveEntry GetNextEntryTar ()
215 TarInputStream tar_stream = (TarInputStream) archive_stream;
216 TarEntry tar_entry;
218 do {
219 tar_entry = tar_stream.GetNextEntry ();
220 } while (tar_entry != null && tar_entry.IsDirectory);
222 // End of the entries;
223 if (tar_entry == null)
224 return null;
226 ArchiveEntry entry = new ArchiveEntry ();
227 entry.Name = tar_entry.Name;
228 entry.Modified = tar_entry.ModTime;
230 entry.TempFile = StoreStreamInTempFile (archive_stream, entry.Modified);
231 entry.MimeType = XdgMime.GetMimeType (entry.TempFile);
233 return entry;
236 private bool handled_single = false;
238 private ArchiveEntry GetNextEntrySingle ()
240 if (handled_single)
241 return null;
243 ArchiveEntry entry = new ArchiveEntry ();
244 entry.Name = Path.GetFileNameWithoutExtension (this.file_info.Name);
245 entry.Modified = this.file_info.LastWriteTimeUtc;
247 entry.TempFile = StoreStreamInTempFile (archive_stream, entry.Modified);
248 entry.MimeType = XdgMime.GetMimeType (entry.TempFile);
250 handled_single = true;
252 return entry;