Reading emails from Gmail in C#
Asked Answered
I

4

40

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://mcmap.net/q/396311/-reading-emails-from-gmail-in-c

Investigate answered 14/8, 2011 at 12:8 Comment(3)
You can access Gmail via via IMAP. See this question: https://mcmap.net/q/187416/-accessing-imap-in-c-closedFundamentalism
@Kiquenet Final working version posted below: https://mcmap.net/q/396311/-reading-emails-from-gmail-in-cInvestigate
Mark as final answer then.Feldt
I
55

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,
                            "[email protected]",
                            "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, "[email protected]", "YOURPASSWORDHERE");
    var allEmails = mailRepository.GetAllMails();

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

    Assert.IsTrue(allEmails.ToList().Any());
}
Investigate answered 24/10, 2013 at 15:44 Comment(12)
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?Singlestick
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.Investigate
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 accounts.google.com/signin/… Web login required. 161116082452398 NO [ALERT] Please log in via your web browser: support.google.com/mail/accounts/answer/78754 (Failure)**Lunarian
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: google.com/settings/security/lesssecureappsLegalize
I have copy pasted your code. Exception thrown: 'System.IndexOutOfRangeException' in ActiveUp.Net.Imap4.dll when it tries get mail via GetAllMails("inbox");Collinsworth
what is ? IMailRepository. Mailkit does not have that Interface.Collinsworth
@Collinsworth This was from my code base. You can remove that interface for your own use-case.Investigate
can I limit GetAllMails by date?Marcasite
@Marcasite Yes, it is definitely possible. You should really post a different question on this site though.Investigate
@Investigate 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!Stadiometer
When I try to read the UnreadEmail I get the exception on GetMail method with exception like index out of exceptionNuptials
Could not load type System.Net.ICertificatePolicy Microsoft has obsolesced that library. Code changes to ActiveUp are needed. learn.microsoft.com/en-us/dotnet/api/…Bannon
M
25

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}");
}
Millepede answered 21/8, 2013 at 22:35 Comment(7)
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.Mervin
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! :)Millepede
Then please illustrate how you would retrieve the message body.Collinsworth
nr is undefinedAstromancy
Love your answer - Does every single thing need a NuGet package to be viable these days. Is that what we've become :(Winburn
I got a 401 Unauthorized error. How should I fix that?Mania
another problem with this model is that, you cannot get entire mailbox information. max 20 email details. No more than that.Pacha
T
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

Thrombosis answered 14/8, 2011 at 12:16 Comment(1)
Thanks, somehow I managed to try every C# non-working POP3 email client :) thanksInvestigate
I
-2

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.

Inobservance answered 16/8, 2011 at 20:11 Comment(2)
I have no budget to pay for this as there are open-source and free alternatives available.Investigate
any free version, not premium? Any open source alternative ?Feldt

© 2022 - 2024 — McMap. All rights reserved.