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 SentDate
{
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); }
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); }
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
,
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
);
360 uint count
= Decode
.UInt (f
);
365 for (int i
= 0; i
< count
; i
++) {
367 bool part
= PerformContentInfoLoad (f
);
369 throw new Exception ();
374 private bool ContentInfoLoad (FileStream f
)
376 if (f
.ReadByte () == 0)
380 Decode
.SkipToken (f
);
382 Decode
.SkipToken (f
);
385 count
= Decode
.UInt (f
);
389 for (int i
= 0; i
< count
; i
++) {
391 Decode
.SkipToken (f
);
393 Decode
.SkipToken (f
);
397 Decode
.SkipToken (f
);
400 Decode
.SkipToken (f
);
403 Decode
.SkipToken (f
);
411 public class SummaryHeader
{
415 public DateTime time
;
421 public SummaryHeader (FileStream f
)
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)
435 flags
= Decode
.FixedInt (f
);
436 nextuid
= Decode
.FixedInt (f
);
437 time
= Decode
.Time (f
);
438 count
= Decode
.FixedInt (f
);
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);
476 throw new Exception ("IMAP summary version too low");
478 // Right now we only support summary versions 1 through 3
480 throw new Exception (String
.Format ("Reported summary version ({0}) is too new", version
));
486 Decode
.SkipFixedInt (f
);
494 public class Decode
{
495 static Encoding e
= Encoding
.UTF8
;
496 static long UnixBaseTicks
;
500 UnixBaseTicks
= new DateTime (1970, 1, 1, 0, 0, 0).Ticks
;
503 public static string Token (FileStream f
)
505 int len
= (int) UInt (f
);
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 ();
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
);
527 f
.Seek (len
, SeekOrigin
.Current
);
530 public static string String (FileStream f
)
532 int len
= (int) UInt (f
);
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
);
547 f
.Seek (len
, SeekOrigin
.Current
);
550 public static uint UInt (FileStream f
)
555 while (((v
= f
.ReadByte ()) & 0x80) == 0 && v
!= -1){
559 return value | ((byte)(v
& 0x7f));
562 public static int FixedInt (FileStream f
)
564 byte [] b
= new byte [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
)
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);
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];
599 return (uint)((b
[0] << 24) | (b
[1] << 16) | (b
[2] << 8) | b
[3]);
605 void Main (string [] args
)
609 if (args
.Length
== 0)
614 Summary s
= Summary
.LoadMBoxSummary (file
);
615 foreach (MessageInfo m
in s
) {
616 Console
.WriteLine(m
);