2 // camel.cs: Parser for Evolution mbox summary files.
5 // Miguel de Icaza <miguel@ximian.com>
7 // Imap support by Erik Bågfors <erik@bagfors.nu>
12 using System
.Collections
;
13 using System
.Globalization
;
16 namespace Beagle
.Util
{
19 public enum CamelFlags
: uint {
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 ();
43 public static Summary
LoadImapSummary (string file
)
45 Summary s
= new ImapSummary ();
51 public static Summary
LoadImap4Summary (string file
)
53 Summary s
= new Imap4Summary ();
59 private void Load (string file
)
62 FileStream f
= File
.OpenRead (file
);
63 this.header
= this.ReadHeader (f
);
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
{
80 public SummaryEnumerator (Summary s
) {
85 public void Dispose ()
91 GC
.SuppressFinalize (this);
101 public bool MoveNext () {
104 f
= File
.OpenRead (s
.filename
);
105 header
= s
.ReadHeader (f
);
108 if (index
>= header
.count
) {
116 while (info
== null && index
< header
.count
) {
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());
129 return (info
!= null);
133 public object Current
{
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
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
);
237 count
= Decode
.UInt (f
);
239 for (int i
= 0; i
< count
; i
++) {
240 Decode
.SkipFixedInt (f
);
241 Decode
.SkipFixedInt (f
);
246 count
= Decode
.UInt (f
);
248 for (int i
= 0; i
< count
; i
++) {
249 Decode
.SkipString (f
);
254 count
= Decode
.UInt (f
);
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 Date
{
272 get { return received.Ticks != 0 ? received : sent; }
275 private bool CheckFlag (CamelFlags test
)
277 return (flags
& (uint) test
) == (uint) test
;
280 public bool IsAnswered
{
281 get { return CheckFlag (CamelFlags.Answered); }
284 public bool IsDeleted
{
285 get { return CheckFlag (CamelFlags.Deleted); }
288 public bool IsDraft
{
289 get { return CheckFlag (CamelFlags.Draft); }
292 public bool IsFlagged
{
293 get { return CheckFlag (CamelFlags.Flagged); }
297 get { return CheckFlag (CamelFlags.Seen); }
300 public bool HasAttachments
{
301 get { return CheckFlag (CamelFlags.Attachments); }
304 public bool IsAnsweredAll
{
305 get { return CheckFlag (CamelFlags.AnsweredAll); }
309 get { return CheckFlag (CamelFlags.Junk); }
312 public bool IsSecure
{
313 get { return CheckFlag (CamelFlags.Secure); }
317 public class MBoxMessageInfo
: MessageInfo
{
318 public uint from_pos
;
320 public MBoxMessageInfo (FileStream f
) : base (f
)
322 from_pos
= Decode
.Offset (f
);
325 public override string ToString ()
327 return String
.Format ("From: {0}\nTo: {1}\nSubject: {2}\nPos: {3} Size: {4}\n", from, to
, subject
,
334 public class ImapMessageInfo
: MessageInfo
{
335 public uint server_flags
;
337 public ImapMessageInfo (FileStream f
, bool content_info_load
) : base (f
)
339 server_flags
= Decode
.UInt (f
);
341 if (content_info_load
)
342 PerformContentInfoLoad (f
);
345 public override string ToString ()
347 return String
.Format ("From: {0}\nTo: {1}\nSubject: {2}\nSize: {3}\n", from, to
, subject
, size
);
350 private bool PerformContentInfoLoad (FileStream f
)
352 bool ci
= ContentInfoLoad (f
);
356 uint count
= Decode
.UInt (f
);
357 if (count
== -1 || count
> 500) {
361 for (int i
= 0; i
< count
; i
++) {
363 bool part
= PerformContentInfoLoad (f
);
365 throw new Exception ();
370 private bool ContentInfoLoad (FileStream f
)
372 if (f
.ReadByte () == 0)
376 Decode
.SkipToken (f
);
378 Decode
.SkipToken (f
);
381 count
= Decode
.UInt (f
);
382 if (count
== -1 || count
> 500)
384 for (int i
= 0; i
< count
; i
++) {
386 Decode
.SkipToken (f
);
388 Decode
.SkipToken (f
);
392 Decode
.SkipToken (f
);
395 Decode
.SkipToken (f
);
398 Decode
.SkipToken (f
);
406 public class SummaryHeader
{
410 public DateTime time
;
416 public SummaryHeader (FileStream f
)
420 version
= Decode
.FixedInt (f
);
422 if (version
> 0xff && (version
& 0xff) < 12)
423 throw new Exception ("Summary header version too low");
425 if (version
< 0x100 && version
>= 13)
430 flags
= Decode
.FixedInt (f
);
431 nextuid
= Decode
.FixedInt (f
);
432 time
= Decode
.Time (f
);
433 count
= Decode
.FixedInt (f
);
436 unread
= Decode
.FixedInt (f
);
437 deleted
= Decode
.FixedInt (f
);
438 junk
= Decode
.FixedInt (f
);
441 //Console.WriteLine ("V={0} ({1}) time={2}, count={3} unread={4} deleted={5} junk={6}", version, version & 0xff, time, count, unread, deleted, junk);
445 public class MBoxSummaryHeader
: SummaryHeader
{
446 public int local_version
;
447 public int mbox_version
;
448 public int folder_size
;
450 public MBoxSummaryHeader (FileStream f
) : base (f
)
452 local_version
= Decode
.FixedInt (f
);
453 mbox_version
= Decode
.FixedInt (f
);
454 folder_size
= Decode
.FixedInt (f
);
456 //Console.WriteLine ("local_version={0} mbox_version={1} folder_size={2}", local_version, mbox_version, folder_size);
460 public class ImapSummaryHeader
: SummaryHeader
{
462 public ImapSummaryHeader (FileStream f
) : base (f
)
464 // Check for legacy version
465 if (base.version
!= 0x30c) { // 780
466 int version
= Decode
.FixedInt (f
);
468 //Console.WriteLine ("imap version={0}", version);
471 throw new Exception ("IMAP summary version too low");
473 // Right now we only support summary versions 1 through 3
475 throw new Exception (String
.Format ("Reported summary version ({0}) is too new", version
));
481 Decode
.SkipFixedInt (f
);
489 public class Decode
{
490 static Encoding e
= Encoding
.UTF8
;
491 static long UnixBaseTicks
;
495 UnixBaseTicks
= new DateTime (1970, 1, 1, 0, 0, 0).Ticks
;
498 public static string Token (FileStream f
)
500 int len
= (int) UInt (f
);
505 // Ok, this is a token from the list, we can ignore it
506 return "token_from_list";
507 } else if (len
< 0 || len
> 10240) {
508 throw new Exception ();
511 byte [] buffer
= new byte [len
];
512 f
.Read (buffer
, 0, (int) len
);
513 return new System
.String (e
.GetChars (buffer
, 0, len
));
517 public static void SkipToken (FileStream f
)
519 int len
= (int) UInt (f
);
522 f
.Seek (len
, SeekOrigin
.Current
);
525 public static string String (FileStream f
)
527 int len
= (int) UInt (f
);
530 if (len
< 0 || len
> 65535)
531 throw new Exception ();
532 byte [] buffer
= new byte [len
];
533 f
.Read (buffer
, 0, (int) len
);
534 return new System
.String (e
.GetChars (buffer
, 0, len
));
537 public static void SkipString (FileStream f
)
539 int len
= (int) UInt (f
);
542 f
.Seek (len
, SeekOrigin
.Current
);
545 public static uint UInt (FileStream f
)
550 while (((v
= f
.ReadByte ()) & 0x80) == 0 && v
!= -1){
554 return value | ((byte)(v
& 0x7f));
557 public static int FixedInt (FileStream f
)
559 byte [] b
= new byte [4];
563 return (b
[0] << 24) | (b
[1] << 16) | (b
[2] << 8) | b
[3];
566 public static void SkipFixedInt (FileStream f
)
568 f
.Seek (4, SeekOrigin
.Current
);
571 public static DateTime
Time (FileStream f
)
575 // FIXME: Is it safe to assume that sizeof (time_t) == IntPtr.Size? Probably not.
576 for (int i
= IntPtr
.Size
- 1; i
>= 0; i
--) {
577 int v
= f
.ReadByte ();
579 seconds
|= v
<< (i
* 8);
583 return new DateTime (0);
585 return new DateTime (UnixBaseTicks
).AddSeconds (seconds
);
588 public static uint Offset (FileStream f
)
590 byte [] b
= new byte [4];
594 return (uint)((b
[0] << 24) | (b
[1] << 16) | (b
[2] << 8) | b
[3]);
600 void Main (string [] args
)
604 if (args
.Length
== 0)
609 Summary s
= Summary
.LoadMBoxSummary (file
);
610 foreach (MessageInfo m
in s
) {
611 Console
.WriteLine(m
);