Search code examples
c#emaildirectorymailkit

Moving email in Mailkit from one folder to another folder is not working


I am using ImapClient by MailKit and trying to move a message from folder A to folder B after reading the message, but the message is not moved and still exists in folder A. What is the problem?

Here is my code:

using (var client = new ImapClient())
{
    client.Connect(_config.SmtpServer, _config.Port, true);
    client.Authenticate(_config.UserName, _config.Password);
    client.Inbox.Open(MailKit.FolderAccess.ReadWrite);
    var inbox = client;

    var archive = inbox.GetFolder("B");
    archive.Open(FolderAccess.ReadWrite);

    var A = inbox.GetFolder("A");
    A.Open(FolderAccess.ReadWrite);

    for (int i = 0; i < A.Count; i++)
    {
      if (!A.IsOpen)
          A.Open(FolderAccess.ReadWrite);
      MimeMessage message = A.GetMessage(i);
      //reading email
      //....
      A.MoveTo(i, archive); //this line doesn't affect the message
    }

    client.Disconnect(true);
}

The function A.MoveTo(i, archive) doesn't move the email.

Thank you if you can help me in this case.


Solution

  • If the IMAP server doesn't support the MOVE command, then MailKit implements the MoveTo method as a COPY and marks the message for deletion (STORE {index} +FLAGS.SILENT (\Deleted)).

    You can check if your IMAP server supports the MOVE command with the following code:

    if (client.Capabilities.HasFlag(ImapCapabilities.Move)) {
        // ...
    }
    

    Looking at your code, I see a few things you should change:

    1. You do not need to call Open(FolderAccess.ReadWrite) for the archive folder (aka folder 'B') (same for the client.Inbox right after authenticating). In fact, don't do that, it's a waste of bandwidth. Note that IMAP only allows 1 folder to be open at a time and the folder you have open should only be the folder that you are reading from. Do not open the destination folder for a CopyTo() or MoveTo() (or Append(), but in Append()'s case, it's ok if you open that folder if you also plan to grab messages or message summary info from it).
    2. Not sure why you are doing if (!A.IsOpen) A.Open(FolderAccess.ReadWrite); in your loop - that makes me wonder if you are closing the folder (or otherwise causing it to get closed by opening another folder) as part of your processing.
    3. I really recommend avoiding accessing messages by their index in the folder and strongly recommend using UIDs. In one of your comments, you said, "Also I need to sort the emails based on oldest to newest first before for loop". If that is the case, here's how I would recommend doing that while using UIDs:
    using (var client = new ImapClient())
    {
        client.Connect(_config.SmtpServer, _config.Port, true);
        client.Authenticate(_config.UserName, _config.Password);
    
        var inbox = client.Inbox;
        var archive = inbox.GetFolder("B");
        var A = inbox.GetFolder("A");
    
        // we only Open() folder 'A' because we plan to retrieve message data from it.
        A.Open(FolderAccess.ReadWrite);
    
        // Note: the default order of UIDs returned by Search() is by Arrival,
        // so if that's all you want, you can just use Search(SearchQuery.All)
        // instead. The Sort() method is the same as Search(), but allows
        // customizing the sort order of the message UIDs returned. You can sort
        // by Arrival, Date, Subject, etc or the Reverse of those and you can
        // also combine them to sort on several fields.
        //var orderBy = new OrderBy[] { OrderBy.Arrival };
        //var uids = A.Sort(SearchQuery.All, orderBy);
        var uids = A.Search(SearchQuery.All);
    
        foreach (var uid in uids)
        {
          MimeMessage message = A.GetMessage(uid);
    
          //reading email
          //....
    
          A.MoveTo(uid, archive);
        }
    
        client.Disconnect(true);
    }