0

I've been using this code of MailSystem.Net so far to get emails from an Imap INBOX and added options to retrieve mail using $"SENTSINCE {Date}".

string mailBox = "INBOX";
public IEnumerable<Message> GetMailsSince(string mailBox) {
  return GetMails(mailBox, $"SENTSINCE {DateTime.Now.AddDays(-3).ToString("dd-MMM-yyyy")}").Cast<Message>();
}

private MessageCollection GetMails(string mailBox, string searchPhrase) {
  Mailbox mails = Client.SelectMailbox(mailBox);
  MessageCollection messages = mails.SearchParse(searchPhrase);
  return messages;
}

But even after studying mailkit for hours I can't seem to distill out how to do the same thing. My goal is to get a list of message object whose properties I can then map to another class I created which writes it into a mysql database. I also want to save the attachments to disk. All this works fine so far but performance is an issue. I'm hoping mailkit will greatly improve that.

My main source has been the sample here but because I'm not familiar with async programming yet and treeviews it's hard to see through it.

How can I hard code that I want "INBOX" as ´IMailFolder´? Where or how can I use the "SENTSINCE {Date}" filter in Mailkit? How do I get an ´IEnumerable´ of Mailkits equivalent to the Message object in mailsystem (´IMessageSummary´ maybe)?

If you can point me to some code or even convert the linked MailSystem.Net example to Mailkit that would be fantastic.

penCsharpener
  • 409
  • 7
  • 15

2 Answers2

3

MimeMessage is the equivalent of MailSystem.NET's Message object, but that's not what you want. What you want is MailKit's IMessageSummary which will allow you to download individual MIME parts (aka "attachments").

It also allows you to get summary information about the message (flags, received date (aka "InternalDate") pre-parsed/decoded common header values (such as subject, sender, recipients, etc) really quickly because the IMAP server has those pieces of information cached in its database for quick retrieval.

using (var client = new ImapClient ()) {
    client.Connect ("imap.mail-server.com", 993, SecureSocketOptions.SslOnConnect);
    client.Authenticate ("username", "password");

    // if you don't care about modifying message flags or deleting
    // messages, you can open the INBOX in read-only mode...
    client.Inbox.Open (FolderAccess.ReadOnly);

    // search for messages sent since a particular date
    var uids = client.Inbox.Search (SearchQuery.SentAfter (date));

    // using the uids of the matching messages, fetch the BODYSTRUCTUREs
    // of each message so that we can figure out which MIME parts to
    // download individually.
    foreach (var item in client.Inbox.Fetch (uids, MessageSummaryItems.BodyStructure MessageSummaryItems.UniqueId)) {
        foreach (var attachment in item.Attachments.OfType<BodyPartBasic> ()) {
            var part = (MimePart) client.Inbox.GetBodyPart (item.UniqueId, attachment);

            using (var stream = File.Create (part.FileName))
                part.ContentObject.DecodeTo (stream);
        }
    }
}

Note: Each property on IMessageSummary has a corresponding MessageSummaryItems enum value that you will need to use in order to have that property populated.

For example, if you want to use IMessageSummary.Envelope, you will need to include MessageSummaryItems.Envelope in your Fetch() request.

Since MessageSummaryItems is marked with the [Flags] attribute, you can bitwise-or enum values together like this:

MessageSummaryItems.BodyStructure | MessageSummaryItems.Envelope and both pieces of information will be fetched.

Update:

Here's the inefficient way that is closer to how MailSystem.NET does it.

using (var client = new ImapClient ()) {
    client.Connect ("imap.mail-server.com", 993, SecureSocketOptions.SslOnConnect);
    client.Authenticate ("username", "password");

    // if you don't care about modifying message flags or deleting
    // messages, you can open the INBOX in read-only mode...
    client.Inbox.Open (FolderAccess.ReadOnly);

    // search for messages sent since a particular date
    var uids = client.Inbox.Search (SearchQuery.SentAfter (date));

    // using the uids of the matching messages, fetch the BODYSTRUCTUREs
    // of each message so that we can figure out which MIME parts to
    // download individually.
    foreach (var uid in uids) {
        var message = client.Inbox.GetMessage (uid);

        foreach (var attachment in message.Attachments.OfType<MimePart> ()) {
            using (var stream = File.Create (attachment.FileName))
                attachment.ContentObject.DecodeTo (stream);
        }
    }
}

Note: if you care about saving message/rfc822 attachments, then take a look at this StackOverflow answer: MailKit save Attachments

jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • thanks for this. This looks great but I'm still out of my depth. Will have to take this away and study the properties you are using here. Also I believe that `ImapFolder.ReadOnly` should be `FolderAccess.ReadOnly`. At least that's what i found in your Xamarin documentation before. I wouldn't mind having something that's inefficient for the beginning as long as it works like mailsystem.net does right now. Then I can continue optimizing. I will check out the mimekit as well. Thanks for the hint. – penCsharpener Oct 30 '17 at 21:27
  • Sorry, yes, I meant FolderAccess.ReadOnly. I'll update my answer to show you the *inefficient* way to do this as well. – jstedfast Oct 31 '17 at 14:20
  • Thanks for the *inefficient* snippet. It will help me getting my head around mailkit. Starting from there I will work my way up to the efficient method ;-) – penCsharpener Nov 06 '17 at 11:13
1

The "Inbox" folder is always available on an IMAP mail acccount. With MailKit it is available as ImapClient.Inbox. For the date filtering you could use the DateSearchQuery class. The getting started page of MailKit pretty much covers all your questions.

Hintham
  • 1,078
  • 10
  • 29
  • thanks, a link would have been good as there are multiple getting started pages I found. But since I'm still new to c# programming (4months) such extensive documentation (which is actually great to have) is hard to digest. I did try but there was just too much for me to figure out what I need. – penCsharpener Oct 30 '17 at 20:54