39

I am trying to read emails from Gmail. I have tried every API / open source project I can find, and can not get any of them working.

Does anyone have a sample of working code that will allow me to authenticate and download emails from a Gmail account?

Final working version posted below: https://stackoverflow.com/a/19570553/550198

Community
  • 1
  • 1
Seany84
  • 5,526
  • 5
  • 42
  • 67

4 Answers4

53

Using the library from: https://github.com/pmengal/MailSystem.NET

Here is my complete code sample:

Email Repository

using System.Collections.Generic;
using System.Linq;
using ActiveUp.Net.Mail;

namespace GmailReadImapEmail
{
    public class MailRepository
    {
        private Imap4Client client;

        public MailRepository(string mailServer, int port, bool ssl, string login, string password)
        {
            if (ssl)
                Client.ConnectSsl(mailServer, port);
            else
                Client.Connect(mailServer, port);
            Client.Login(login, password);
        }

        public IEnumerable<Message> GetAllMails(string mailBox)
        {
            return GetMails(mailBox, "ALL").Cast<Message>();
        }

        public IEnumerable<Message> GetUnreadMails(string mailBox)
        {
            return GetMails(mailBox, "UNSEEN").Cast<Message>();
        }

        protected Imap4Client Client
        {
            get { return client ?? (client = new Imap4Client()); }
        }

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

Usage

[TestMethod]
public void ReadImap()
{
    var mailRepository = new MailRepository(
                            "imap.gmail.com",
                            993,
                            true,
                            "yourEmailAddress@gmail.com",
                            "yourPassword"
                        );

    var emailList = mailRepository.GetAllMails("inbox");

    foreach (Message email in emailList)
    {
        Console.WriteLine("<p>{0}: {1}</p><p>{2}</p>", email.From, email.Subject, email.BodyHtml.Text);
        if (email.Attachments.Count > 0)
        {
            foreach (MimePart attachment in email.Attachments)
            {
                Console.WriteLine("<p>Attachment: {0} {1}</p>", attachment.ContentName, attachment.ContentType.MimeType);
            }
        }
    }
}

Another example, this time using MailKit

public class MailRepository : IMailRepository
{
    private readonly string mailServer, login, password;
    private readonly int port;
    private readonly bool ssl;

    public MailRepository(string mailServer, int port, bool ssl, string login, string password)
    {
        this.mailServer = mailServer;
        this.port = port;
        this.ssl = ssl;
        this.login = login;
        this.password = password;
    }

    public IEnumerable<string> GetUnreadMails()
    {
        var messages = new List<string>();

        using (var client = new ImapClient())
        {
            client.Connect(mailServer, port, ssl);

            // Note: since we don't have an OAuth2 token, disable
            // the XOAUTH2 authentication mechanism.
            client.AuthenticationMechanisms.Remove("XOAUTH2");

            client.Authenticate(login, password);

            // The Inbox folder is always available on all IMAP servers...
            var inbox = client.Inbox;
            inbox.Open(FolderAccess.ReadOnly);
            var results = inbox.Search(SearchOptions.All, SearchQuery.Not(SearchQuery.Seen));
            foreach (var uniqueId in results.UniqueIds)
            {
                var message = inbox.GetMessage(uniqueId);

                messages.Add(message.HtmlBody);

                //Mark message as read
                //inbox.AddFlags(uniqueId, MessageFlags.Seen, true);
            }

            client.Disconnect(true);
        }

        return messages;
    }

    public IEnumerable<string> GetAllMails()
    {
        var messages = new List<string>();

        using (var client = new ImapClient())
        {
            client.Connect(mailServer, port, ssl);

            // Note: since we don't have an OAuth2 token, disable
            // the XOAUTH2 authentication mechanism.
            client.AuthenticationMechanisms.Remove("XOAUTH2");

            client.Authenticate(login, password);

            // The Inbox folder is always available on all IMAP servers...
            var inbox = client.Inbox;
            inbox.Open(FolderAccess.ReadOnly);
            var results = inbox.Search(SearchOptions.All, SearchQuery.NotSeen);
            foreach (var uniqueId in results.UniqueIds)
            {
                var message = inbox.GetMessage(uniqueId);

                messages.Add(message.HtmlBody);

                //Mark message as read
                //inbox.AddFlags(uniqueId, MessageFlags.Seen, true);
            }

            client.Disconnect(true);
        }

        return messages;
    }
}

Usage

[Test]
public void GetAllEmails()
{
    var mailRepository = new MailRepository("imap.gmail.com", 993, true, "YOUREMAILHERE@gmail.com", "YOURPASSWORDHERE");
    var allEmails = mailRepository.GetAllMails();

    foreach(var email in allEmails)
    {
        Console.WriteLine(email);
    }

    Assert.IsTrue(allEmails.ToList().Any());
}
Seany84
  • 5,526
  • 5
  • 42
  • 67
  • Just tried your code, it compiles okay. But when I try to run, it got stuck for more than 5 minutes at the line mails.SearchParse(searchPhrase) via call GetUnreadMails. I only have 53 unread emails in my inbox. Any explanation? – newman Sep 26 '14 at 03:24
  • I have not used this code since around the time of the OP. However, I fired up my unit test and it did retrieve my unreal emails (count=12). Test took approximately 66 seconds to run. – Seany84 Sep 26 '14 at 09:00
  • 1
    I have build the console application in c# using above code, everything fixed but gmail not allowing to login. Here is my error message:- ** failed : * NO [WEBALERT https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbvAEG9lCWQe-Z6jvg-VMfEebVSPMJ8KdejU_3XhJcIi8AlYr4JO8PCByg3WoSo2JePxUXqnYowDn-O5NCkPcL62ywOQgBtk9mZEZQn6YxbKdafk2MyUtx29A85MOF9Duz3MHYdNIHjSAl_p9S2QjchFBIiVxUDAiZOBsbz3Lk] Web login required. 161116082452398 NO [ALERT] Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)** – Gurpreet Singh Nov 16 '16 at 14:56
  • 3
    In order for this to work, you need to enable "Less secure apps" with your gmail account, if that is an option for you. I did that, and now the code works. Read more here: https://www.google.com/settings/security/lesssecureapps – Kjensen Feb 26 '17 at 09:52
  • 1
    I have copy pasted your code. Exception thrown: 'System.IndexOutOfRangeException' in ActiveUp.Net.Imap4.dll when it tries get mail via GetAllMails("inbox"); – Nulle Oct 14 '17 at 16:09
  • 2
    what is ? IMailRepository. Mailkit does not have that Interface. – Nulle Oct 14 '17 at 16:30
  • 2
    @Nulle This was from my code base. You can remove that interface for your own use-case. – Seany84 Oct 14 '17 at 17:15
  • can I limit GetAllMails by date? – Toolkit Jan 27 '19 at 14:05
  • @Toolkit Yes, it is definitely possible. You should really post a different question on this site though. – Seany84 Jan 27 '19 at 23:18
  • @Seany84 thanks for your nice sample codes. I tried to use `MailSystem.NET` , but has error on Client.Log() : BAD Too many arguments provided . My password contains spaces and this login fails! – MohaMad Aug 24 '19 at 14:26
  • When I try to read the UnreadEmail I get the exception on GetMail method with exception like index out of exception – Ashok kumar Ganesan Jun 05 '20 at 12:57
  • Could not load type System.Net.ICertificatePolicy Microsoft has obsolesced that library. Code changes to ActiveUp are needed. https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.certificatepolicy?view=netframework-4.7.2 – D. Kermott Mar 04 '23 at 14:30
23

You don't need any extra 3rd Party Libraries if a summary of the 20 most recent emails is sufficient for you. You can read the data from API that Gmail has provided here: https://mail.google.com/mail/feed/atom

The response in XML format can be handled by the code below:

try {
    const string emailAddress = "YourEmail";
    // App Password, not password
    // See: https://support.google.com/accounts/answer/185833?hl=en
    const string appPassword = "YourAppPassword";

    string response;
    string title;
    string summary;
    XmlDocument xmlDocument = new XmlDocument();

    HttpClient httpClient = new HttpClient();

    // Logging in Gmail server to get data
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{emailAddress}:{appPassword}")));

    // Reading data and converting to string
    response = await httpClient.GetStringAsync(@"https://mail.google.com/mail/feed/atom");

    // Remove XML namespace to simplify parsing/selecting nodes
    response = response.Replace(@"<feed version=""0.3"" xmlns=""http://purl.org/atom/ns#"">", @"<feed>");

    // Loading into an XML so we can get information easily
    xmlDocument.LoadXml(response);

    // Amount of emails
    string nr = xmlDocument.SelectSingleNode(@"/feed/fullcount").InnerText;

    // Reading the title and the summary for every email
    foreach (XmlNode node in xmlDocument.SelectNodes(@"/feed/entry")) {
        title = node.SelectSingleNode("title").InnerText;
        summary = node.SelectSingleNode("summary").InnerText;

        Console.WriteLine($"> {title}");
        Console.WriteLine($"{summary}");
        Console.WriteLine();
    }
} catch (Exception ex) {
    MessageBox.Show($"Error retrieving mails: {ex.Message}");
}
KoalaBear
  • 2,755
  • 2
  • 25
  • 29
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
  • 3
    The only problem with this approach, and it's important to many people, is that you can't get the full text of the message body via the ATOM route. – Eric Oct 11 '13 at 01:26
  • 1
    There is another method of getting the route of ATOM with XML. You just have to create a namespace for XML form and you can access it easily! Read about that and you will understand because its no difference from this! :) – E-Riddie Oct 11 '13 at 07:29
  • Then please illustrate how you would retrieve the message body. – Nulle Oct 14 '17 at 15:55
  • nr is undefined – Bengi Besçeli Feb 09 '18 at 21:54
  • 2
    Love your answer - Does every single thing need a NuGet package to be viable these days. Is that what we've become :( – Mr. B Apr 30 '19 at 07:14
  • 1
    I got a 401 Unauthorized error. How should I fix that? – Yijun Yuan Sep 05 '19 at 06:21
  • another problem with this model is that, you cannot get entire mailbox information. max 20 email details. No more than that. – Albert Arul prakash Apr 22 '20 at 18:45
5

Have you tried POP3 Email Client with full MIME Support ?

If you don't it's a very good example for you. As an alternativ;

OpenPop.NET

.NET class library in C# for communicating with POP3 servers. Easy to use but yet powerful. Includes a robust MIME parser backed by several hundred test cases. For more information, visit our project homepage.

Lumisoft

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
-1

You can also try Mail.dll IMAP client.

It supports all Gmail IMAP protocol extensions:

  • Thread ID,
  • Message ID,
  • Labels,
  • Localized folder names,
  • Google search syntax
  • OAuth authentication.

Please note that Mail.dll is a commercial product, I've developed.

Pawel Lesnikowski
  • 6,264
  • 4
  • 38
  • 42