+1 vote
by (260 points)
edited

I working on a project to copy messages (IMAP) from Gmail to another platform.

As Gmail uses the concept of tags (instead Folders) and each tag is mapped as a IMAP folder, a message with multiples tags will be present in multiple IMAP folders.

So, the same message will processed in each folder/tag with a unnecessary overhead.

How can I detect a message on folder y is the same I already processed on folder x? with this information I can copy the message inside destination server, instead make a new copy from the source, accelerating the process and preserving bandwidth.

I tried work with the UniqueId property , but it not seems to be the path to the solution.

Applies to: Rebex Secure Mail

2 Answers

+1 vote
by (58.9k points)
edited
 
Best answer

Here is a method which will enable you to detect the same messages + sample use:

    /// <summary>
    /// Gets the Gmail X-GM-MSGID based on the Sequence number of the message.
    /// </summary>
    /// <param name="imap">The connected and authenticated Imap client instance, assuming the right folder is selected.</param>
    /// <param name="sequenceNumber">The mail sequence number.</param>
    /// <returns>The X-GM-MSGID as defined in ">https://developers.google.com/gmail/imap_extensions#access_to_the_gmail_unique_message_id_x-gm-msgid</returns>
    static string GetGmailMsgId(Imap imap, int sequenceNumber)
    {
        imap.SendCommand("FETCH", sequenceNumber.ToString(), "(X-GM-MSGID)");
        ImapResponse response = imap.ReadResponse();

        if (response.Code != 0)
            throw new ImapException(response);

        ImapResponseLine[] lines = response.GetLines();
        if (lines.Length != 1)
            throw new ImapException("Unexpected number of response lines.");

        string reply = lines[0].Parameters;

        int i = reply.IndexOf(" (X-GM-MSGID ") + " (X-GM-MSGID ".Length;
        return reply.Substring(i).TrimEnd(new char[] { ')' });
    }

    static void ImapFoldersTestOnGmailPublic()
    {
        Imap imap = new Imap();
        imap.Connect("imap.gmail.com", SslMode.Implicit);
        imap.Login("uername@gmail.com", "password");

        imap.SelectFolder("Folder1");

        var messages = imap.Search(ImapSearchParameter.Subject("Hi!"));
        string id = string.Empty;

        if (messages.Count > 0)
        {
            // retrieve X-GM-MSGID for the first found message from Folder1
            var messageInfo = messages[0];

            Console.WriteLine("Email from '{0}' folder", imap.CurrentFolder.Name);
            Console.WriteLine("Unique ID: " + messageInfo.UniqueId);
            id = GetGmailMsgId(imap, messageInfo.SequenceNumber);
            Console.WriteLine("Gmail ID: " + id);
        }

        imap.SelectFolder("Folder2");
        messages = imap.Search(ImapSearchParameter.Subject("Hi!"));

        if (messages.Count > 0)
        {
            // retrieve X-GM-MSGID for the first found message from Folder2
            var messageInfo = messages[0];
            Console.WriteLine("Email from '{0}' folder", imap.CurrentFolder.Name);

            Console.WriteLine("Unique ID: " + messageInfo.UniqueId);
            string id2 = GetGmailMsgId(imap, messageInfo.SequenceNumber);
            Console.WriteLine("Gmail ID: " + id2);

            Console.WriteLine(id.Equals(id2) ? "This is actually one message marked with two labels: 'Folder1' and 'Folder2.'": "The found messages are different.");
        }

        imap.Disconnect();
    }

In your data migrating program you just have to remember the X-GM-MSGID of already proccesed messages and then see if a currently processed message was not proccesed already by looking up a Dictionary for instance. Please note that the above GetGmailMsgId method can only be used with Gmail IMAP servers.

+1 vote
by (58.9k points)
edited

Hello,

Gmail offers a so called Gmail unique message id, see more at https://developers.google.com/gmail/imap_extensions#access_to_the_gmail_unique_message_id_x-gm-msgid

This id stays the same for the same message appearing in different Imap folders.

You can access it via FETCH command with X-GM-MSGID parameter. I will post complete code which does it using Imap.SendCommand and Imap.ReadResponse methods of Rebex.NET.Imap object for you tomorrow.

...