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 override string ToString ()
70 return "No header read";
72 return header
.ToString ();
75 public IEnumerator
GetEnumerator () {
76 return new SummaryEnumerator (this);
79 protected abstract SummaryHeader
ReadHeader (FileStream f
);
80 protected abstract MessageInfo
ReadMessageInfo (FileStream f
);
82 private class SummaryEnumerator
: IEnumerator
, IDisposable
{
88 public SummaryEnumerator (Summary s
) {
93 public void Dispose ()
99 GC
.SuppressFinalize (this);
102 ~
SummaryEnumerator ()
109 public bool MoveNext () {
112 f
= File
.OpenRead (s
.filename
);
113 header
= s
.ReadHeader (f
);
116 if (index
>= header
.count
) {
124 while (info
== null && index
< header
.count
) {
126 info
= s
.ReadMessageInfo (f
);
127 } catch (Exception e
) {
128 Log
.Warn ("Skipping bogus message " +
129 "[file={0}, index={1}, error={2}]",
130 s
.filename
, index
, e
.ToString());
137 return (info
!= null);
141 public object Current
{
154 public class MBoxSummary
: Summary
{
155 public MBoxSummary ()
159 protected override SummaryHeader
ReadHeader (FileStream f
)
161 return new MBoxSummaryHeader (f
);
164 protected override MessageInfo
ReadMessageInfo (FileStream f
)
166 return new MBoxMessageInfo (f
);
170 public class ImapSummary
: Summary
{
171 public ImapSummary ()
175 protected override SummaryHeader
ReadHeader (FileStream f
)
177 return new ImapSummaryHeader (f
);
180 protected override MessageInfo
ReadMessageInfo (FileStream f
)
182 return new ImapMessageInfo (f
, true);
186 public class Imap4Summary
: Summary
{
187 public Imap4Summary ()
191 protected override SummaryHeader
ReadHeader (FileStream f
)
193 return new ImapSummaryHeader (f
);
196 protected override MessageInfo
ReadMessageInfo (FileStream f
)
198 return new ImapMessageInfo (f
, false);
202 public class MessageInfo
{
203 public string uid
, subject
, from, to
, cc
, mlist
;
204 public uint size
, flags
;
205 public DateTime sent
, received
;
207 private void SkipContentInfo (FileStream f
)
209 Decode
.SkipToken (f
); // type
210 Decode
.SkipToken (f
); // subtype
211 uint count
= Decode
.UInt (f
); // count
212 for (int i
= 0; i
< count
; ++i
) {
213 Decode
.SkipToken (f
); // name
214 Decode
.SkipToken (f
); // value
216 Decode
.SkipToken (f
); // id
217 Decode
.SkipToken (f
); // description
218 Decode
.SkipToken (f
); // encoding
219 Decode
.UInt (f
); // size
221 count
= Decode
.UInt (f
); // child count
222 for (int i
= 0; i
< count
; ++i
) // recursively skip children
226 public MessageInfo (FileStream f
)
228 uid
= Decode
.String (f
);
229 flags
= Decode
.UInt (f
);
230 size
= Decode
.UInt (f
);
231 sent
= Decode
.Time (f
);
232 received
= Decode
.Time (f
);
233 subject
= Decode
.String (f
);
234 from = Decode
.String (f
);
235 to
= Decode
.String (f
);
236 cc
= Decode
.String (f
);
237 mlist
= Decode
.String (f
);
239 Decode
.SkipFixedInt (f
);
240 Decode
.SkipFixedInt (f
);
245 count
= Decode
.UInt (f
);
247 for (int i
= 0; i
< count
; i
++) {
248 Decode
.SkipFixedInt (f
);
249 Decode
.SkipFixedInt (f
);
254 count
= Decode
.UInt (f
);
256 for (int i
= 0; i
< count
; i
++) {
257 Decode
.SkipString (f
);
262 count
= Decode
.UInt (f
);
264 for (int i
= 0; i
< count
; i
++){
265 Decode
.SkipString (f
);
266 Decode
.SkipString (f
);
270 // FIXME: How do we know if there is content info in there?
271 // SkipContentInfo (f);
274 public override string ToString ()
276 return String
.Format ("From: {0}\nTo: {1}\nSubject: {2}\nUID: {3}\n", from, to
, subject
, uid
);
279 public DateTime SentDate
{
283 public DateTime ReceivedDate
{
284 get { return received; }
287 private bool CheckFlag (CamelFlags test
)
289 return (flags
& (uint) test
) == (uint) test
;
292 public bool IsAnswered
{
293 get { return CheckFlag (CamelFlags.Answered); }
296 public bool IsDeleted
{
297 get { return CheckFlag (CamelFlags.Deleted); }
300 public bool IsDraft
{
301 get { return CheckFlag (CamelFlags.Draft); }
304 public bool IsFlagged
{
305 get { return CheckFlag (CamelFlags.Flagged); }
309 get { return CheckFlag (CamelFlags.Seen); }
312 public bool HasAttachments
{
313 get { return CheckFlag (CamelFlags.Attachments); }
316 public bool IsAnsweredAll
{
317 get { return CheckFlag (CamelFlags.AnsweredAll); }
321 get { return CheckFlag (CamelFlags.Junk); }
324 public bool IsSecure
{
325 get { return CheckFlag (CamelFlags.Secure); }
329 public class MBoxMessageInfo
: MessageInfo
{
330 public uint from_pos
;
332 public MBoxMessageInfo (FileStream f
) : base (f
)
334 from_pos
= Decode
.Offset (f
);
337 public override string ToString ()
339 return String
.Format ("From: {0}\nTo: {1}\nSubject: {2}\nPos: {3} Size: {4}\n", from, to
, subject
,
344 public class ImapMessageInfo
: MessageInfo
{
345 public uint server_flags
;
347 public ImapMessageInfo (FileStream f
, bool content_info_load
) : base (f
)
349 server_flags
= Decode
.UInt (f
);
351 if (content_info_load
)
352 PerformContentInfoLoad (f
);
355 public override string ToString ()
357 return String
.Format ("From: {0}\nTo: {1}\nSubject: {2}\nSize: {3}\n", from, to
, subject
, size
);
360 private bool PerformContentInfoLoad (FileStream f
)
362 bool ci
= ContentInfoLoad (f
);
366 uint count
= Decode
.UInt (f
);
372 for (int i
= 0; i
< count
; i
++) {
374 bool part
= PerformContentInfoLoad (f
);
376 throw new Exception ();
381 private bool ContentInfoLoad (FileStream f
)
383 if (f
.ReadByte () == 0)
387 Decode
.SkipToken (f
);
389 Decode
.SkipToken (f
);
392 count
= Decode
.UInt (f
);
396 for (int i
= 0; i
< count
; i
++) {
398 Decode
.SkipToken (f
);
400 Decode
.SkipToken (f
);
404 Decode
.SkipToken (f
);
407 Decode
.SkipToken (f
);
410 Decode
.SkipToken (f
);
418 public class SummaryHeader
{
422 public DateTime time
;
428 public SummaryHeader (FileStream f
)
432 version
= Decode
.FixedInt (f
);
434 if (version
> 0xff && (version
& 0xff) < 12)
435 throw new Exception ("Summary header version too low");
437 if (version
< 0x100 && version
>= 13)
442 flags
= Decode
.FixedInt (f
);
443 nextuid
= Decode
.FixedInt (f
);
444 time
= Decode
.Time (f
);
445 count
= Decode
.FixedInt (f
);
448 unread
= Decode
.FixedInt (f
);
449 deleted
= Decode
.FixedInt (f
);
450 junk
= Decode
.FixedInt (f
);
454 public override string ToString ()
456 return String
.Format ("version={0} flags={1} nextuid={2} time={3} count={4} unread={5} deleted={6} junk={7}",
457 version
, flags
, nextuid
, time
, count
, unread
, deleted
, junk
);
461 public class MBoxSummaryHeader
: SummaryHeader
{
462 public int local_version
;
463 public int mbox_version
;
464 public int folder_size
;
466 public MBoxSummaryHeader (FileStream f
) : base (f
)
468 local_version
= Decode
.FixedInt (f
);
469 mbox_version
= Decode
.FixedInt (f
);
470 folder_size
= Decode
.FixedInt (f
);
473 public override string ToString ()
475 return String
.Format ("{0} local_version={1} mbox_version={2} folder_size={3}",
476 base.ToString (), local_version
, mbox_version
, folder_size
);
480 public class ImapSummaryHeader
: SummaryHeader
{
481 private int imap_version
;
483 public ImapSummaryHeader (FileStream f
) : base (f
)
485 // Check for legacy version
486 if (base.version
!= 0x30c) { // 780
487 imap_version
= Decode
.FixedInt (f
);
489 if (imap_version
< 0)
490 throw new Exception ("IMAP summary version too low");
492 // Right now we only support summary versions 1 through 3
493 if (imap_version
> 3)
494 throw new Exception (String
.Format ("Reported summary version ({0}) is too new", imap_version
));
496 if (imap_version
== 2)
500 Decode
.SkipFixedInt (f
);
507 public override string ToString ()
509 return String
.Format ("{0} imap_version={1}", base.ToString (), imap_version
);
513 public class Decode
{
514 static Encoding e
= Encoding
.UTF8
;
515 static long UnixBaseTicks
;
519 //UnixBaseTicks = DateTimeUtil.UnixToDateTimeUtc (0).Ticks;
520 UnixBaseTicks
= new DateTime (1970, 1, 1, 0, 0, 0).Ticks
;
523 public static string Token (FileStream f
)
525 int len
= (int) UInt (f
);
530 // Ok, this is a token from the list, we can ignore it
531 return "token_from_list";
532 } else if (len
< 0 || len
> 10240) {
533 throw new Exception ();
536 byte [] buffer
= new byte [len
];
537 f
.Read (buffer
, 0, (int) len
);
538 return new System
.String (e
.GetChars (buffer
, 0, len
));
542 public static void SkipToken (FileStream f
)
544 int len
= (int) UInt (f
);
547 f
.Seek (len
, SeekOrigin
.Current
);
550 public static string String (FileStream f
)
552 int len
= (int) UInt (f
);
555 if (len
< 0 || len
> 65535)
556 throw new Exception ();
557 byte [] buffer
= new byte [len
];
558 f
.Read (buffer
, 0, (int) len
);
559 return new System
.String (e
.GetChars (buffer
, 0, len
));
562 public static void SkipString (FileStream f
)
564 int len
= (int) UInt (f
);
567 f
.Seek (len
, SeekOrigin
.Current
);
570 public static uint UInt (FileStream f
)
575 while (((v
= f
.ReadByte ()) & 0x80) == 0 && v
!= -1) {
581 throw new Exception ("Unexpected end of file");
583 return value | ((byte)(v
& 0x7f));
586 public static int FixedInt (FileStream f
)
588 byte [] b
= new byte [4];
592 return (b
[0] << 24) | (b
[1] << 16) | (b
[2] << 8) | b
[3];
595 public static void SkipFixedInt (FileStream f
)
597 f
.Seek (4, SeekOrigin
.Current
);
600 public static DateTime
Time (FileStream f
)
604 // FIXME: Is it safe to assume that sizeof (time_t) == IntPtr.Size? Probably not.
605 for (int i
= IntPtr
.Size
- 1; i
>= 0; i
--) {
606 int v
= f
.ReadByte ();
609 throw new Exception ("Unexpected end of file");
611 seconds
|= (uint) v
<< (i
* 8);
615 return new DateTime (0);
617 return new DateTime (UnixBaseTicks
).AddSeconds (seconds
);
620 public static uint Offset (FileStream f
)
622 byte [] b
= new byte [4];
626 return (uint)((b
[0] << 24) | (b
[1] << 16) | (b
[2] << 8) | b
[3]);
633 public static void Main (string [] args
)
637 if (args
.Length
== 0)
642 Summary s
= Summary
.LoadImapSummary (file
);
643 Console
.WriteLine (s
);
644 Console
.WriteLine ();
645 foreach (MessageInfo m
in s
) {
646 Console
.WriteLine(m
);