Search code examples
c#multithreadingasp.net-coreimapmailkit

MailKit: Trying to implement IMAP Client in ASP.NET Core for Receiving new mail event


I'm trying to implement an IMAP client in my ASP.NET Core web project so that it can run on its own thread, whenever a new email comes the MailRecieved event will get fired and I'm receiving parsing the email body and saving in DB.

But the issue is I have to make my MailClient Keep-Alive so that I can keep on listening new emails, Which is not working in my case.I read on code sample in MailKit documentation where they are using System.Timer, unfortunately, is ASP.NET there is no timer.

Here is the code that I'm using:-

 public static async Task MailSubscribe(IMAPConnection connection)
    {
        try
        {
            using (var client = new ImapClient())
            {
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                await client.ConnectAsync(connection.Host, connection.Port, connection.EnableSSL);
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                if (client.IsConnected)
                    client.Authenticate(connection.UserName, connection.Password);
                if (client.IsAuthenticated)
                {
                    var inbox = client.Inbox;
                    inbox.Open(FolderAccess.ReadOnly);
                    inbox.MessagesArrived += async (s, e) =>
                    {
                        using (var mailFetch = new ImapClient())
                        {
                            mailFetch.ServerCertificateValidationCallback = (g, c, h, k) => true;
                            await mailFetch.ConnectAsync(connection.Host, connection.Port, connection.EnableSSL);
                            mailFetch.AuthenticationMechanisms.Remove("XOAUTH2");
                            if (mailFetch.IsConnected)
                                mailFetch.Authenticate(connection.UserName, connection.Password);
                            if (mailFetch.IsAuthenticated)
                            {
                                mailFetch.Inbox.Open(FolderAccess.ReadOnly);
                                var mailIds = mailFetch.Inbox.Search(SearchQuery.NotSeen);
                                foreach (var id in mailIds)
                                {
                                    var mail = mailFetch.Inbox.GetMessage(id);
                                    var htmlDoc = new HtmlDocument();
                                    htmlDoc.LoadHtml(mail.HtmlBody);
                                    var context = htmlDoc.GetElementbyId("context")?.InnerText;
                                    if (context != null)
                                    {
                                        var entity = context;
                                    }
                                }
                            }
                        };
                    };
                }
                using (var done = new CancellationTokenSource())
                {
                    var task = client.IdleAsync(done.Token);
                    int timeout = client.Timeout;
                    while (true)
                    {
                        Thread.Sleep(10000);
                        if (client.IsIdle)
                        {
                            if (!client.Inbox.IsOpen)
                                client.Inbox.Open(FolderAccess.ReadOnly);
                           client.Idle(done.Token);
                        }
                    }
                    // done.Cancel();
                    // task.Wait();
                }
                //client.Disconnect(true);
            };
        }
        catch (Exception ex)
        {
            string exception = ex.Message;
            string innerexception = ex.InnerException.ToString();
        }
    }

Solution

  • There's a CancellationTokenSource constructor that takes an int timeout value that you can use. I think that's available for ASP.NET Core.

    If not, try something more like this:

    while (true) {
        if (!client.Inbox.IsOpen)
            client.Inbox.Open(FolderAccess.ReadOnly);
        var task = client.IdleAsync (done.Token);
        Thread.Sleep(10000);
        done.Cancel();
        task.Wait();
    }