* Filters/FilterPackage.cs, Filters/FilterRPM.cs,
[beagle.git] / Util / camel.cs
blob707515b83ac20e8bfd8016bfa50866a9fbce68d9
1 //
2 // camel.cs: Parser for Evolution mbox summary files.
3 //
4 // Authors:
5 // Miguel de Icaza <miguel@ximian.com>
6 //
7 // Imap support by Erik Bågfors <erik@bagfors.nu>
8 //
10 using System.IO;
11 using System;
12 using System.Collections;
13 using System.Globalization;
14 using System.Text;
16 namespace Beagle.Util {
17 namespace Camel {
19 public enum CamelFlags : uint {
20 Answered = 1 << 0,
21 Deleted = 1 << 1,
22 Draft = 1 << 2,
23 Flagged = 1 << 3,
24 Seen = 1 << 4,
25 Attachments = 1 << 5,
26 AnsweredAll = 1 << 6,
27 Junk = 1 << 7,
28 Secure = 1 << 8
31 public abstract class Summary : IEnumerable {
32 public SummaryHeader header;
33 internal string filename;
35 public static Summary LoadMBoxSummary (string file)
37 Summary s = new MBoxSummary ();
38 s.Load (file);
40 return s;
43 public static Summary LoadImapSummary (string file)
45 Summary s = new ImapSummary ();
46 s.Load (file);
48 return s;
51 public static Summary LoadImap4Summary (string file)
53 Summary s = new Imap4Summary ();
54 s.Load (file);
56 return s;
59 private void Load (string file)
61 this.filename = file;
62 FileStream f = File.OpenRead (file);
63 this.header = this.ReadHeader (f);
64 f.Close ();
67 public IEnumerator GetEnumerator () {
68 return new SummaryEnumerator (this);
71 protected abstract SummaryHeader ReadHeader (FileStream f);
72 protected abstract MessageInfo ReadMessageInfo (FileStream f);
74 private class SummaryEnumerator : IEnumerator, IDisposable {
75 FileStream f = null;
76 SummaryHeader header;
77 Summary s;
78 int index;
79 MessageInfo info;
80 public SummaryEnumerator (Summary s) {
81 this.s = s;
82 index = -1;
85 public void Dispose ()
87 if (f != null) {
88 f.Close ();
89 f = null;
91 GC.SuppressFinalize (this);
94 ~SummaryEnumerator ()
96 if (f != null) {
97 f.Close ();
101 public bool MoveNext () {
102 ++index;
103 if (index == 0) {
104 f = File.OpenRead (s.filename);
105 header = s.ReadHeader (f);
108 if (index >= header.count) {
109 if (f != null) {
110 f.Close ();
111 f = null;
113 return false;
114 } else {
115 info = null;
116 while (info == null && index < header.count) {
117 try {
118 info = s.ReadMessageInfo (f);
119 } catch (Exception e) {
120 Logger.Log.Warn ("Skipping bogus message " +
121 "[file={0}, index={1}, error={2}]",
122 s.filename, index, e.ToString());
124 info = null;
125 ++index;
129 return (info != null);
133 public object Current {
134 get { return info; }
137 public void Reset ()
139 f.Close ();
140 f = null;
141 index = -1;
146 public class MBoxSummary: Summary {
147 public MBoxSummary ()
151 protected override SummaryHeader ReadHeader (FileStream f)
153 return new MBoxSummaryHeader (f);
156 protected override MessageInfo ReadMessageInfo (FileStream f)
158 return new MBoxMessageInfo (f);
162 public class ImapSummary: Summary {
163 public ImapSummary ()
167 protected override SummaryHeader ReadHeader (FileStream f)
169 return new ImapSummaryHeader (f);
172 protected override MessageInfo ReadMessageInfo (FileStream f)
174 return new ImapMessageInfo (f, true);
178 public class Imap4Summary : Summary {
179 public Imap4Summary ()
183 protected override SummaryHeader ReadHeader (FileStream f)
185 return new ImapSummaryHeader (f);
188 protected override MessageInfo ReadMessageInfo (FileStream f)
190 return new ImapMessageInfo (f, false);
194 public class MessageInfo {
195 public string uid, subject, from, to, cc, mlist;
196 public uint size, flags;
197 public DateTime sent, received;
199 private void SkipContentInfo (FileStream f)
201 Decode.SkipToken (f); // type
202 Decode.SkipToken (f); // subtype
203 uint count = Decode.UInt (f); // count
204 for (int i = 0; i < count; ++i) {
205 Decode.SkipToken (f); // name
206 Decode.SkipToken (f); // value
208 Decode.SkipToken (f); // id
209 Decode.SkipToken (f); // description
210 Decode.SkipToken (f); // encoding
211 Decode.UInt (f); // size
213 count = Decode.UInt (f); // child count
214 for (int i = 0; i < count; ++i) // recursively skip children
215 SkipContentInfo (f);
218 public MessageInfo (FileStream f)
220 uid = Decode.String (f);
221 flags = Decode.UInt (f);
222 size = Decode.UInt (f);
223 sent = Decode.Time (f);
224 received = Decode.Time (f);
225 subject = Decode.String (f);
226 from = Decode.String (f);
227 to = Decode.String (f);
228 cc = Decode.String (f);
229 mlist = Decode.String (f);
231 Decode.SkipFixedInt (f);
232 Decode.SkipFixedInt (f);
234 uint count;
236 // references
237 count = Decode.UInt (f);
238 if (count > 0) {
239 for (int i = 0; i < count; i++) {
240 Decode.SkipFixedInt (f);
241 Decode.SkipFixedInt (f);
245 // user flags
246 count = Decode.UInt (f);
247 if (count > 0) {
248 for (int i = 0; i < count; i++) {
249 Decode.SkipString (f);
253 // user tags
254 count = Decode.UInt (f);
255 if (count > 0){
256 for (int i = 0; i < count; i++){
257 Decode.SkipString (f);
258 Decode.SkipString (f);
262 // FIXME: How do we know if there is content info in there?
263 // SkipContentInfo (f);
266 public override string ToString ()
268 return String.Format ("From: {0}\nTo: {1}\nSubject: {2}\nUID: {3}\n", from, to, subject, uid);
271 public DateTime SentDate {
272 get { return sent; }
275 public DateTime ReceivedDate {
276 get { return received; }
279 private bool CheckFlag (CamelFlags test)
281 return (flags & (uint) test) == (uint) test;
284 public bool IsAnswered {
285 get { return CheckFlag (CamelFlags.Answered); }
288 public bool IsDeleted {
289 get { return CheckFlag (CamelFlags.Deleted); }
292 public bool IsDraft {
293 get { return CheckFlag (CamelFlags.Draft); }
296 public bool IsFlagged {
297 get { return CheckFlag (CamelFlags.Flagged); }
300 public bool IsSeen {
301 get { return CheckFlag (CamelFlags.Seen); }
304 public bool HasAttachments {
305 get { return CheckFlag (CamelFlags.Attachments); }
308 public bool IsAnsweredAll {
309 get { return CheckFlag (CamelFlags.AnsweredAll); }
312 public bool IsJunk {
313 get { return CheckFlag (CamelFlags.Junk); }
316 public bool IsSecure {
317 get { return CheckFlag (CamelFlags.Secure); }
321 public class MBoxMessageInfo : MessageInfo {
322 public uint from_pos;
324 public MBoxMessageInfo (FileStream f) : base (f)
326 from_pos = Decode.Offset (f);
329 public override string ToString ()
331 return String.Format ("From: {0}\nTo: {1}\nSubject: {2}\nPos: {3} Size: {4}\n", from, to, subject,
332 from_pos, size);
338 public class ImapMessageInfo : MessageInfo {
339 public uint server_flags;
341 public ImapMessageInfo (FileStream f, bool content_info_load) : base (f)
343 server_flags = Decode.UInt (f);
345 if (content_info_load)
346 PerformContentInfoLoad (f);
349 public override string ToString ()
351 return String.Format ("From: {0}\nTo: {1}\nSubject: {2}\nSize: {3}\n", from, to, subject, size);
354 private bool PerformContentInfoLoad (FileStream f)
356 bool ci = ContentInfoLoad (f);
357 if (!ci)
358 return false;
360 uint count = Decode.UInt (f);
361 if (count > 500) {
362 return false;
365 for (int i = 0; i < count; i++) {
367 bool part = PerformContentInfoLoad (f);
368 if (!part)
369 throw new Exception ();
371 return true;
374 private bool ContentInfoLoad (FileStream f)
376 if (f.ReadByte () == 0)
377 return true;
379 // type
380 Decode.SkipToken (f);
381 // subtype
382 Decode.SkipToken (f);
384 uint count;
385 count = Decode.UInt (f);
386 if (count > 500)
387 return false;
389 for (int i = 0; i < count; i++) {
390 // Name
391 Decode.SkipToken (f);
392 // Value
393 Decode.SkipToken (f);
396 // id
397 Decode.SkipToken (f);
399 // description
400 Decode.SkipToken (f);
402 // encoding
403 Decode.SkipToken (f);
405 // size
406 Decode.UInt (f);
407 return true;
411 public class SummaryHeader {
412 public int version;
413 public int flags;
414 public int nextuid;
415 public DateTime time;
416 public int count;
417 public int unread;
418 public int deleted;
419 public int junk;
421 public SummaryHeader (FileStream f)
423 bool legacy;
425 version = Decode.FixedInt (f);
427 if (version > 0xff && (version & 0xff) < 12)
428 throw new Exception ("Summary header version too low");
430 if (version < 0x100 && version >= 13)
431 legacy = false;
432 else
433 legacy = true;
435 flags = Decode.FixedInt (f);
436 nextuid = Decode.FixedInt (f);
437 time = Decode.Time (f);
438 count = Decode.FixedInt (f);
440 if (!legacy) {
441 unread = Decode.FixedInt (f);
442 deleted = Decode.FixedInt (f);
443 junk = Decode.FixedInt (f);
446 //Console.WriteLine ("V={0} ({1}) time={2}, count={3} unread={4} deleted={5} junk={6}", version, version & 0xff, time, count, unread, deleted, junk);
450 public class MBoxSummaryHeader : SummaryHeader {
451 public int local_version;
452 public int mbox_version;
453 public int folder_size;
455 public MBoxSummaryHeader (FileStream f) : base (f)
457 local_version = Decode.FixedInt (f);
458 mbox_version = Decode.FixedInt (f);
459 folder_size = Decode.FixedInt (f);
461 //Console.WriteLine ("local_version={0} mbox_version={1} folder_size={2}", local_version, mbox_version, folder_size);
465 public class ImapSummaryHeader : SummaryHeader {
467 public ImapSummaryHeader (FileStream f) : base (f)
469 // Check for legacy version
470 if (base.version != 0x30c) { // 780
471 int version = Decode.FixedInt (f);
473 //Console.WriteLine ("imap version={0}", version);
475 if (version < 0)
476 throw new Exception ("IMAP summary version too low");
478 // Right now we only support summary versions 1 through 3
479 if (version > 3)
480 throw new Exception (String.Format ("Reported summary version ({0}) is too new", version));
482 if (version == 2)
483 Decode.FixedInt (f);
485 // validity
486 Decode.SkipFixedInt (f);
487 } else {
488 // validity
489 Decode.UInt (f);
494 public class Decode {
495 static Encoding e = Encoding.UTF8;
496 static long UnixBaseTicks;
498 static Decode ()
500 UnixBaseTicks = new DateTime (1970, 1, 1, 0, 0, 0).Ticks;
503 public static string Token (FileStream f)
505 int len = (int) UInt (f);
506 if (len < 32) {
507 if (len <= 0)
508 return "NULL";
510 // Ok, this is a token from the list, we can ignore it
511 return "token_from_list";
512 } else if (len < 0 || len > 10240) {
513 throw new Exception ();
514 } else {
515 len -= 32;
516 byte [] buffer = new byte [len];
517 f.Read (buffer, 0, (int) len);
518 return new System.String (e.GetChars (buffer, 0, len));
522 public static void SkipToken (FileStream f)
524 int len = (int) UInt (f);
525 len -= 32;
526 if (len > 0)
527 f.Seek (len, SeekOrigin.Current);
530 public static string String (FileStream f)
532 int len = (int) UInt (f);
533 len--;
535 if (len < 0 || len > 65535)
536 throw new Exception ();
537 byte [] buffer = new byte [len];
538 f.Read (buffer, 0, (int) len);
539 return new System.String (e.GetChars (buffer, 0, len));
542 public static void SkipString (FileStream f)
544 int len = (int) UInt (f);
545 --len;
546 if (len > 0)
547 f.Seek (len, SeekOrigin.Current);
550 public static uint UInt (FileStream f)
552 uint value = 0;
553 int v;
555 while (((v = f.ReadByte ()) & 0x80) == 0 && v != -1){
556 value |= (byte) v;
557 value <<= 7;
559 return value | ((byte)(v & 0x7f));
562 public static int FixedInt (FileStream f)
564 byte [] b = new byte [4];
566 f.Read (b, 0, 4);
568 return (b [0] << 24) | (b [1] << 16) | (b [2] << 8) | b [3];
571 public static void SkipFixedInt (FileStream f)
573 f.Seek (4, SeekOrigin.Current);
576 public static DateTime Time (FileStream f)
578 long seconds = 0;
580 // FIXME: Is it safe to assume that sizeof (time_t) == IntPtr.Size? Probably not.
581 for (int i = IntPtr.Size - 1; i >= 0; i--) {
582 int v = f.ReadByte ();
584 seconds |= v << (i * 8);
587 if (seconds == 0)
588 return new DateTime (0);
590 return new DateTime (UnixBaseTicks).AddSeconds (seconds);
593 public static uint Offset (FileStream f)
595 byte [] b = new byte [4];
597 f.Read (b, 0, 4);
599 return (uint)((b [0] << 24) | (b [1] << 16) | (b [2] << 8) | b [3]);
603 #if false
604 class Test {
605 void Main (string [] args)
607 string file;
609 if (args.Length == 0)
610 file = "./summary";
611 else
612 file = args [0];
614 Summary s = Summary.LoadMBoxSummary (file);
615 foreach (MessageInfo m in s) {
616 Console.WriteLine(m);
622 #endif